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 {
25632563 .cpu = .{
25642564 .arch = cpu_arch,
25652565 .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,
25672567 },
25682568 .os = .{
25692569 .tag = os_tag,
lib/std/atomic.zig+2
......@@ -160,6 +160,8 @@ test Value {
160160}
161161
162162test "Value.swap" {
163 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
164
163165 var x = Value(usize).init(5);
164166 try testing.expectEqual(@as(usize, 5), x.swap(10, .seq_cst));
165167 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) {
514514 i64_atomic_rmw32_add_u = 0x24,
515515 i32_atomic_rmw_sub = 0x25,
516516 i64_atomic_rmw_sub = 0x26,
517 i32_atomic_rmw8_sub_u = 0x27A,
518 i32_atomic_rmw16_sub_u = 0x28A,
519 i64_atomic_rmw8_sub_u = 0x29A,
517 i32_atomic_rmw8_sub_u = 0x27,
518 i32_atomic_rmw16_sub_u = 0x28,
519 i64_atomic_rmw8_sub_u = 0x29,
520520 i64_atomic_rmw16_sub_u = 0x2A,
521521 i64_atomic_rmw32_sub_u = 0x2B,
522522 i32_atomic_rmw_and = 0x2C,
src/codegen/wasm/CodeGen.zig+81-25
......@@ -1338,11 +1338,21 @@ const SimdStoreStrategy = enum {
13381338/// it using a instruction, rather than an unrolled version.
13391339pub fn determineSimdStoreStrategy(ty: Type, zcu: *const Zcu, target: *const std.Target) SimdStoreStrategy {
13401340 assert(ty.zigTypeTag(zcu) == .vector);
1341 if (ty.bitSize(zcu) != 128) return .unrolled;
1342 if (target.cpu.has(.wasm, .relaxed_simd) or target.cpu.has(.wasm, .simd128)) {
1343 return .direct;
1344 }
1345 return .unrolled;
1341 if (!target.cpu.has(.wasm, .simd128)) return .unrolled;
1342 return switch (ty.toIntern()) {
1343 .vector_16_i8_type,
1344 .vector_16_u8_type,
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 };
13461356}
13471357
13481358fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5188,12 +5198,27 @@ fn lowerConstant(cg: *CodeGen, val: Value) InnerError!WValue {
51885198 } else {
51895199 return .{ .imm32 = @intFromBool(!val.isNull(zcu)) };
51905200 },
5191 .aggregate => switch (ip.indexToKey(ty.ip_index)) {
5192 .array_type => return cg.fail("Wasm TODO: LowerConstant for {f}", .{ty.fmt(zcu)}),
5193 .vector_type => {
5201 .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {
5202 .array_type => unreachable,
5203 .vector_type => |vector_type| {
51945204 assert(determineSimdStoreStrategy(ty, zcu, cg.target) == .direct);
51955205 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 }
51975222 return cg.storeSimdImmd(buf);
51985223 },
51995224 .struct_type => unreachable, // packed structs use `bitpack`
......@@ -5216,6 +5241,7 @@ fn storeSimdImmd(cg: *CodeGen, value: [16]u8) !WValue {
52165241fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue {
52175242 const zcu = cg.pt.zcu;
52185243 switch (ty.zigTypeTag(zcu)) {
5244 .vector => return cg.storeSimdImmd(@splat(0xaa)),
52195245 .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa },
52205246 .int, .@"enum" => switch (ty.intInfo(zcu).bits) {
52215247 0...32 => return .{ .imm32 = 0xaaaaaaaa },
......@@ -5785,7 +5811,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro
57855811 .f80, .f128 => unreachable,
57865812 },
57875813 .int => {},
5788 .aggregate => unreachable,
5814 .aggregate => return operand, // both v128
57895815 }
57905816
57915817 switch (dest_class) {
......@@ -6991,22 +7017,49 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69917017 const result: WValue = result_value: {
69927018 switch (result_ty.zigTypeTag(zcu)) {
69937019 .array, .vector => {
6994 const result = try cg.allocStack(result_ty);
69957020 const elem_ty = result_ty.childType(zcu);
6996 const elem_size = elem_ty.abiSize(zcu);
6997 const sentinel = result_ty.sentinel(zcu);
6998
6999 var offset: u64 = 0;
7000 for (elements) |elem| {
7001 const elem_val = try cg.resolveInst(elem);
7002 try cg.store(result, elem_val, elem_ty, .{ .offset = offset });
7003 offset += elem_size;
7004 }
7005 if (sentinel) |s| {
7006 const val = try cg.resolveValue(s);
7007 try cg.store(result, val, elem_ty, .{ .offset = offset });
7021 if (isByRef(result_ty, zcu, cg.target)) {
7022 const result = try cg.allocStack(result_ty);
7023 const elem_size = elem_ty.abiSize(zcu);
7024 const sentinel = result_ty.sentinel(zcu);
7025
7026 var offset: u64 = 0;
7027 for (elements) |elem| {
7028 const elem_val = try cg.resolveInst(elem);
7029 try cg.store(result, elem_val, elem_ty, .{ .offset = offset });
7030 offset += elem_size;
7031 }
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;
70087062 }
7009 break :result_value result;
70107063 },
70117064 .@"struct" => switch (result_ty.containerLayout(zcu)) {
70127065 .@"packed" => unreachable, // legalize .expand_packed_aggregate_init
......@@ -7633,7 +7686,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
76337686 1 => .i32_atomic_rmw8_cmpxchg_u,
76347687 2 => .i32_atomic_rmw16_cmpxchg_u,
76357688 4 => .i32_atomic_rmw_cmpxchg,
7636 8 => .i32_atomic_rmw_cmpxchg,
7689 8 => .i64_atomic_rmw_cmpxchg,
76377690 else => |size| return cg.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}),
76387691 }, .{
76397692 .offset = ptr_operand.memOffset(),
......@@ -7721,6 +7774,9 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
77217774 const op: std.lang.AtomicRmwOp = extra.op();
77227775
77237776 if (cg.useAtomicFeature()) {
7777 if (ty.isAnyFloat()) {
7778 return cg.fail("TODO: atomic rmw on floats", .{});
7779 }
77247780 const int_ty: IntType = .fromType(cg, ty);
77257781 switch (op) {
77267782 .Max,
test/behavior/atomics.zig+1
......@@ -184,6 +184,7 @@ fn testAtomicStore() !void {
184184}
185185
186186test "atomicrmw with floats" {
187 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
187188 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
188189 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
189190 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/tests.zig+21
......@@ -1612,6 +1612,27 @@ const module_test_targets = blk: {
16121612 .{
16131613 .target = .{
16141614 .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 },
16151636 .os_tag = .wasi,
16161637 .abi = .none,
16171638 },