authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-09-30 20:57:45+03:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-10-20 23:10:13+02:00
log2d361f612b426fa44861caa2ff54c716a4dd5c89
tree6cf95fe037cb066ce772180a62c348d246292733
parentf7f3d24984deb81deb9bcfe99b4eb37e8d7215bf

wasm: test and fix compilation of mvp/lime1/wasm_v3_0 mcpu


6 files changed, 109 insertions(+), 29 deletions(-)

lib/std/Build/Configuration.zig+1-1
...@@ -2563,7 +2563,7 @@ pub const TargetQuery = struct {...@@ -2563,7 +2563,7 @@ pub const TargetQuery = struct {
2563 .cpu = .{2563 .cpu = .{
2564 .arch = cpu_arch,2564 .arch = cpu_arch,
2565 .model = cpu_arch.parseCpuModel(tq.cpu_name.value.?.slice(c)).?,2565 .model = cpu_arch.parseCpuModel(tq.cpu_name.value.?.slice(c)).?,
2566 .features = tq.cpu_features_add.value.?,2566 .features = tq.cpu_features_add.value orelse .empty,
2567 },2567 },
2568 .os = .{2568 .os = .{
2569 .tag = os_tag,2569 .tag = os_tag,
lib/std/atomic.zig+2
...@@ -160,6 +160,8 @@ test Value {...@@ -160,6 +160,8 @@ test Value {
160}160}
161161
162test "Value.swap" {162test "Value.swap" {
163 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
164
163 var x = Value(usize).init(5);165 var x = Value(usize).init(5);
164 try testing.expectEqual(@as(usize, 5), x.swap(10, .seq_cst));166 try testing.expectEqual(@as(usize, 5), x.swap(10, .seq_cst));
165 try testing.expectEqual(@as(usize, 10), x.load(.seq_cst));167 try testing.expectEqual(@as(usize, 10), x.load(.seq_cst));
lib/std/wasm.zig+3-3
...@@ -514,9 +514,9 @@ pub const AtomicsOpcode = enum(u32) {...@@ -514,9 +514,9 @@ pub const AtomicsOpcode = enum(u32) {
514 i64_atomic_rmw32_add_u = 0x24,514 i64_atomic_rmw32_add_u = 0x24,
515 i32_atomic_rmw_sub = 0x25,515 i32_atomic_rmw_sub = 0x25,
516 i64_atomic_rmw_sub = 0x26,516 i64_atomic_rmw_sub = 0x26,
517 i32_atomic_rmw8_sub_u = 0x27A,517 i32_atomic_rmw8_sub_u = 0x27,
518 i32_atomic_rmw16_sub_u = 0x28A,518 i32_atomic_rmw16_sub_u = 0x28,
519 i64_atomic_rmw8_sub_u = 0x29A,519 i64_atomic_rmw8_sub_u = 0x29,
520 i64_atomic_rmw16_sub_u = 0x2A,520 i64_atomic_rmw16_sub_u = 0x2A,
521 i64_atomic_rmw32_sub_u = 0x2B,521 i64_atomic_rmw32_sub_u = 0x2B,
522 i32_atomic_rmw_and = 0x2C,522 i32_atomic_rmw_and = 0x2C,
src/codegen/wasm/CodeGen.zig+81-25
...@@ -1338,11 +1338,21 @@ const SimdStoreStrategy = enum {...@@ -1338,11 +1338,21 @@ const SimdStoreStrategy = enum {
1338/// it using a instruction, rather than an unrolled version.1338/// it using a instruction, rather than an unrolled version.
1339pub fn determineSimdStoreStrategy(ty: Type, zcu: *const Zcu, target: *const std.Target) SimdStoreStrategy {1339pub fn determineSimdStoreStrategy(ty: Type, zcu: *const Zcu, target: *const std.Target) SimdStoreStrategy {
1340 assert(ty.zigTypeTag(zcu) == .vector);1340 assert(ty.zigTypeTag(zcu) == .vector);
1341 if (ty.bitSize(zcu) != 128) return .unrolled;1341 if (!target.cpu.has(.wasm, .simd128)) return .unrolled;
1342 if (target.cpu.has(.wasm, .relaxed_simd) or target.cpu.has(.wasm, .simd128)) {1342 return switch (ty.toIntern()) {
1343 return .direct;1343 .vector_16_i8_type,
1344 }1344 .vector_16_u8_type,
1345 return .unrolled;1345 .vector_8_i16_type,
1346 .vector_8_u16_type,
1347 .vector_4_i32_type,
1348 .vector_4_u32_type,
1349 .vector_2_i64_type,
1350 .vector_2_u64_type,
1351 .vector_4_f32_type,
1352 .vector_2_f64_type,
1353 => .direct,
1354 else => .unrolled,
1355 };
1346}1356}
13471357
1348fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {1358fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -5188,12 +5198,27 @@ fn lowerConstant(cg: *CodeGen, val: Value) InnerError!WValue {...@@ -5188,12 +5198,27 @@ fn lowerConstant(cg: *CodeGen, val: Value) InnerError!WValue {
5188 } else {5198 } else {
5189 return .{ .imm32 = @intFromBool(!val.isNull(zcu)) };5199 return .{ .imm32 = @intFromBool(!val.isNull(zcu)) };
5190 },5200 },
5191 .aggregate => switch (ip.indexToKey(ty.ip_index)) {5201 .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {
5192 .array_type => return cg.fail("Wasm TODO: LowerConstant for {f}", .{ty.fmt(zcu)}),5202 .array_type => unreachable,
5193 .vector_type => {5203 .vector_type => |vector_type| {
5194 assert(determineSimdStoreStrategy(ty, zcu, cg.target) == .direct);5204 assert(determineSimdStoreStrategy(ty, zcu, cg.target) == .direct);
5195 var buf: [16]u8 = undefined;5205 var buf: [16]u8 = undefined;
5196 val.writeToMemory(zcu, &buf) catch unreachable;5206 const elem_size: usize = @intCast(Type.fromInterned(vector_type.child).abiSize(zcu));
5207 for (0..vector_type.len) |i| {
5208 const dest = buf[i * elem_size ..][0..elem_size];
5209 const elem: Value = switch (aggregate.storage) {
5210 .bytes => |bytes| {
5211 dest[0] = bytes.at(i, ip);
5212 continue;
5213 },
5214 .elems => |elems| .fromInterned(elems[i]),
5215 .repeated_elem => |elem| .fromInterned(elem),
5216 };
5217 elem.writeToMemory(zcu, dest) catch |err| switch (err) {
5218 error.OutOfMemory => return error.OutOfMemory,
5219 error.ReinterpretDeclRef, error.IllDefinedMemoryLayout => unreachable,
5220 };
5221 }
5197 return cg.storeSimdImmd(buf);5222 return cg.storeSimdImmd(buf);
5198 },5223 },
5199 .struct_type => unreachable, // packed structs use `bitpack`5224 .struct_type => unreachable, // packed structs use `bitpack`
...@@ -5216,6 +5241,7 @@ fn storeSimdImmd(cg: *CodeGen, value: [16]u8) !WValue {...@@ -5216,6 +5241,7 @@ fn storeSimdImmd(cg: *CodeGen, value: [16]u8) !WValue {
5216fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {5241fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
5217 const zcu = cg.pt.zcu;5242 const zcu = cg.pt.zcu;
5218 switch (ty.zigTypeTag(zcu)) {5243 switch (ty.zigTypeTag(zcu)) {
5244 .vector => return cg.storeSimdImmd(@splat(0xaa)),
5219 .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa },5245 .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa },
5220 .int, .@"enum" => switch (ty.intInfo(zcu).bits) {5246 .int, .@"enum" => switch (ty.intInfo(zcu).bits) {
5221 0...32 => return .{ .imm32 = 0xaaaaaaaa },5247 0...32 => return .{ .imm32 = 0xaaaaaaaa },
...@@ -5785,7 +5811,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro...@@ -5785,7 +5811,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro
5785 .f80, .f128 => unreachable,5811 .f80, .f128 => unreachable,
5786 },5812 },
5787 .int => {},5813 .int => {},
5788 .aggregate => unreachable,5814 .aggregate => return operand, // both v128
5789 }5815 }
57905816
5791 switch (dest_class) {5817 switch (dest_class) {
...@@ -6991,22 +7017,49 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6991,22 +7017,49 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6991 const result: WValue = result_value: {7017 const result: WValue = result_value: {
6992 switch (result_ty.zigTypeTag(zcu)) {7018 switch (result_ty.zigTypeTag(zcu)) {
6993 .array, .vector => {7019 .array, .vector => {
6994 const result = try cg.allocStack(result_ty);
6995 const elem_ty = result_ty.childType(zcu);7020 const elem_ty = result_ty.childType(zcu);
6996 const elem_size = elem_ty.abiSize(zcu);7021 if (isByRef(result_ty, zcu, cg.target)) {
6997 const sentinel = result_ty.sentinel(zcu);7022 const result = try cg.allocStack(result_ty);
69987023 const elem_size = elem_ty.abiSize(zcu);
6999 var offset: u64 = 0;7024 const sentinel = result_ty.sentinel(zcu);
7000 for (elements) |elem| {7025
7001 const elem_val = try cg.resolveInst(elem);7026 var offset: u64 = 0;
7002 try cg.store(result, elem_val, elem_ty, .{ .offset = offset });7027 for (elements) |elem| {
7003 offset += elem_size;7028 const elem_val = try cg.resolveInst(elem);
7004 }7029 try cg.store(result, elem_val, elem_ty, .{ .offset = offset });
7005 if (sentinel) |s| {7030 offset += elem_size;
7006 const val = try cg.resolveValue(s);7031 }
7007 try cg.store(result, val, elem_ty, .{ .offset = offset });7032 if (sentinel) |s| {
7033 const val = try cg.resolveValue(s);
7034 try cg.store(result, val, elem_ty, .{ .offset = offset });
7035 }
7036 break :result_value result;
7037 } else {
7038 assert(result_ty.zigTypeTag(zcu) == .vector);
7039 const splat: std.wasm.SimdOpcode, const replace_lane: std.wasm.SimdOpcode = switch (elem_ty.toIntern()) {
7040 .u8_type, .i8_type => .{ .i8x16_splat, .i8x16_replace_lane },
7041 .u16_type, .i16_type => .{ .i16x8_splat, .i16x8_replace_lane },
7042 .u32_type, .i32_type => .{ .i32x4_splat, .i32x4_replace_lane },
7043 .u64_type, .i64_type => .{ .i64x2_splat, .i64x2_replace_lane },
7044 .f32_type => .{ .f32x4_splat, .f32x4_replace_lane },
7045 .f64_type => .{ .f64x2_splat, .f64x2_replace_lane },
7046 else => unreachable,
7047 };
7048
7049 // TODO: find best element to splat, if it is constant dont use splat at all
7050 try cg.emitWValue(try cg.resolveInst(elements[0]));
7051 const splat_extra: u32 = @intCast(cg.mir_extra.items.len);
7052 try cg.mir_extra.append(cg.gpa, @backingInt(splat));
7053 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = splat_extra } });
7054 for (elements[1..], 1..) |elem, lane| {
7055 if (elem == elements[0]) continue;
7056 try cg.emitWValue(try cg.resolveInst(elem));
7057 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
7058 try cg.mir_extra.appendSlice(cg.gpa, &.{ @backingInt(replace_lane), @intCast(lane) });
7059 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
7060 }
7061 break :result_value .stack;
7008 }7062 }
7009 break :result_value result;
7010 },7063 },
7011 .@"struct" => switch (result_ty.containerLayout(zcu)) {7064 .@"struct" => switch (result_ty.containerLayout(zcu)) {
7012 .@"packed" => unreachable, // legalize .expand_packed_aggregate_init7065 .@"packed" => unreachable, // legalize .expand_packed_aggregate_init
...@@ -7633,7 +7686,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7633,7 +7686,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7633 1 => .i32_atomic_rmw8_cmpxchg_u,7686 1 => .i32_atomic_rmw8_cmpxchg_u,
7634 2 => .i32_atomic_rmw16_cmpxchg_u,7687 2 => .i32_atomic_rmw16_cmpxchg_u,
7635 4 => .i32_atomic_rmw_cmpxchg,7688 4 => .i32_atomic_rmw_cmpxchg,
7636 8 => .i32_atomic_rmw_cmpxchg,7689 8 => .i64_atomic_rmw_cmpxchg,
7637 else => |size| return cg.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}),7690 else => |size| return cg.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}),
7638 }, .{7691 }, .{
7639 .offset = ptr_operand.memOffset(),7692 .offset = ptr_operand.memOffset(),
...@@ -7721,6 +7774,9 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7721,6 +7774,9 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7721 const op: std.lang.AtomicRmwOp = extra.op();7774 const op: std.lang.AtomicRmwOp = extra.op();
77227775
7723 if (cg.useAtomicFeature()) {7776 if (cg.useAtomicFeature()) {
7777 if (ty.isAnyFloat()) {
7778 return cg.fail("TODO: atomic rmw on floats", .{});
7779 }
7724 const int_ty: IntType = .fromType(cg, ty);7780 const int_ty: IntType = .fromType(cg, ty);
7725 switch (op) {7781 switch (op) {
7726 .Max,7782 .Max,
test/behavior/atomics.zig+1
...@@ -184,6 +184,7 @@ fn testAtomicStore() !void {...@@ -184,6 +184,7 @@ fn testAtomicStore() !void {
184}184}
185185
186test "atomicrmw with floats" {186test "atomicrmw with floats" {
187 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
187 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;188 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
188 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO189 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
189 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO190 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/tests.zig+21
...@@ -1612,6 +1612,27 @@ const module_test_targets = blk: {...@@ -1612,6 +1612,27 @@ const module_test_targets = blk: {
1612 .{1612 .{
1613 .target = .{1613 .target = .{
1614 .cpu_arch = .wasm32,1614 .cpu_arch = .wasm32,
1615 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
1616 .os_tag = .wasi,
1617 .abi = .none,
1618 },
1619 .use_llvm = false,
1620 .use_lld = false,
1621 },
1622 .{
1623 .target = .{
1624 .cpu_arch = .wasm32,
1625 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.lime1 },
1626 .os_tag = .wasi,
1627 .abi = .none,
1628 },
1629 .use_llvm = false,
1630 .use_lld = false,
1631 },
1632 .{
1633 .target = .{
1634 .cpu_arch = .wasm32,
1635 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.wasm_v3_0 },
1615 .os_tag = .wasi,1636 .os_tag = .wasi,
1616 .abi = .none,1637 .abi = .none,
1617 },1638 },