authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2025-02-27 02:06:20+01:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2025-03-24 14:58:05+01:00
logca2bd6d6ef55f02100a99b1012529c3804db75d7
tree795ce8e3ee5aeaa90dd0f5d9274fa16b2cf9de61
parent677b2d62e5af4d7a97883994b8d8c45a06535ea0

stage2-wasm: fix comparing and storing optionals


3 files changed, 32 insertions(+), 25 deletions(-)

src/arch/wasm/CodeGen.zig+32-20
...@@ -2361,29 +2361,32 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr...@@ -2361,29 +2361,32 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
2361 .error_union => {2361 .error_union => {
2362 const pl_ty = ty.errorUnionPayload(zcu);2362 const pl_ty = ty.errorUnionPayload(zcu);
2363 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {2363 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2364 return cg.store(lhs, rhs, Type.anyerror, 0);2364 return cg.store(lhs, rhs, Type.anyerror, offset);
2365 }2365 }
23662366
2367 const len = @as(u32, @intCast(abi_size));2367 const len = @as(u32, @intCast(abi_size));
2368 assert(offset == 0);
2368 return cg.memcpy(lhs, rhs, .{ .imm32 = len });2369 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
2369 },2370 },
2370 .optional => {2371 .optional => {
2371 if (ty.isPtrLikeOptional(zcu)) {2372 if (ty.isPtrLikeOptional(zcu)) {
2372 return cg.store(lhs, rhs, Type.usize, 0);2373 return cg.store(lhs, rhs, Type.usize, offset);
2373 }2374 }
2374 const pl_ty = ty.optionalChild(zcu);2375 const pl_ty = ty.optionalChild(zcu);
2375 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {2376 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
2376 return cg.store(lhs, rhs, Type.u8, 0);2377 return cg.store(lhs, rhs, Type.u8, offset);
2377 }2378 }
2378 if (pl_ty.zigTypeTag(zcu) == .error_set) {2379 if (pl_ty.zigTypeTag(zcu) == .error_set) {
2379 return cg.store(lhs, rhs, Type.anyerror, 0);2380 return cg.store(lhs, rhs, Type.anyerror, offset);
2380 }2381 }
23812382
2382 const len = @as(u32, @intCast(abi_size));2383 const len = @as(u32, @intCast(abi_size));
2384 assert(offset == 0);
2383 return cg.memcpy(lhs, rhs, .{ .imm32 = len });2385 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
2384 },2386 },
2385 .@"struct", .array, .@"union" => if (isByRef(ty, zcu, cg.target)) {2387 .@"struct", .array, .@"union" => if (isByRef(ty, zcu, cg.target)) {
2386 const len = @as(u32, @intCast(abi_size));2388 const len = @as(u32, @intCast(abi_size));
2389 assert(offset == 0);
2387 return cg.memcpy(lhs, rhs, .{ .imm32 = len });2390 return cg.memcpy(lhs, rhs, .{ .imm32 = len });
2388 },2391 },
2389 .vector => switch (determineSimdStoreStrategy(ty, zcu, cg.target)) {2392 .vector => switch (determineSimdStoreStrategy(ty, zcu, cg.target)) {
...@@ -2407,6 +2410,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr...@@ -2407,6 +2410,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
2407 },2410 },
2408 .pointer => {2411 .pointer => {
2409 if (ty.isSlice(zcu)) {2412 if (ty.isSlice(zcu)) {
2413 assert(offset == 0);
2410 // store pointer first2414 // store pointer first
2411 // lower it to the stack so we do not have to store rhs into a local first2415 // lower it to the stack so we do not have to store rhs into a local first
2412 try cg.emitWValue(lhs);2416 try cg.emitWValue(lhs);
...@@ -2421,6 +2425,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr...@@ -2421,6 +2425,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
2421 }2425 }
2422 },2426 },
2423 .int, .@"enum", .float => if (abi_size > 8 and abi_size <= 16) {2427 .int, .@"enum", .float => if (abi_size > 8 and abi_size <= 16) {
2428 assert(offset == 0);
2424 try cg.emitWValue(lhs);2429 try cg.emitWValue(lhs);
2425 const lsb = try cg.load(rhs, Type.u64, 0);2430 const lsb = try cg.load(rhs, Type.u64, 0);
2426 try cg.store(.stack, lsb, Type.u64, 0 + lhs.offset());2431 try cg.store(.stack, lsb, Type.u64, 0 + lhs.offset());
...@@ -2430,6 +2435,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr...@@ -2430,6 +2435,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr
2430 try cg.store(.stack, msb, Type.u64, 8 + lhs.offset());2435 try cg.store(.stack, msb, Type.u64, 8 + lhs.offset());
2431 return;2436 return;
2432 } else if (abi_size > 16) {2437 } else if (abi_size > 16) {
2438 assert(offset == 0);
2433 try cg.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) });2439 try cg.memcpy(lhs, rhs, .{ .imm32 = @as(u32, @intCast(ty.abiSize(zcu))) });
2434 },2440 },
2435 else => if (abi_size > 8) {2441 else => if (abi_size > 8) {
...@@ -4438,9 +4444,6 @@ fn airOptionalPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void...@@ -4438,9 +4444,6 @@ fn airOptionalPayloadPtrSet(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void
4438 const operand = try cg.resolveInst(ty_op.operand);4444 const operand = try cg.resolveInst(ty_op.operand);
4439 const opt_ty = cg.typeOf(ty_op.operand).childType(zcu);4445 const opt_ty = cg.typeOf(ty_op.operand).childType(zcu);
4440 const payload_ty = opt_ty.optionalChild(zcu);4446 const payload_ty = opt_ty.optionalChild(zcu);
4441 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4442 return cg.fail("TODO: Implement OptionalPayloadPtrSet for optional with zero-sized type {}", .{payload_ty.fmtDebug()});
4443 }
44444447
4445 if (opt_ty.optionalReprIsPayload(zcu)) {4448 if (opt_ty.optionalReprIsPayload(zcu)) {
4446 return cg.finishAir(inst, operand, &.{ty_op.operand});4449 return cg.finishAir(inst, operand, &.{ty_op.operand});
...@@ -5407,31 +5410,40 @@ fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: st...@@ -5407,31 +5410,40 @@ fn cmpOptionals(cg: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: st
5407 assert(operand_ty.hasRuntimeBitsIgnoreComptime(zcu));5410 assert(operand_ty.hasRuntimeBitsIgnoreComptime(zcu));
5408 assert(op == .eq or op == .neq);5411 assert(op == .eq or op == .neq);
5409 const payload_ty = operand_ty.optionalChild(zcu);5412 const payload_ty = operand_ty.optionalChild(zcu);
5413 assert(!isByRef(payload_ty, zcu, cg.target));
54105414
5411 // We store the final result in here that will be validated5415 var result = try cg.allocLocal(Type.i32);
5412 // if the optional is truly equal.
5413 var result = try cg.ensureAllocLocal(Type.i32);
5414 defer result.free(cg);5416 defer result.free(cg);
54155417
5418 var lhs_null = try cg.allocLocal(Type.i32);
5419 defer lhs_null.free(cg);
5420
5416 try cg.startBlock(.block, .empty);5421 try cg.startBlock(.block, .empty);
5422
5423 try cg.addImm32(if (op == .eq) 0 else 1);
5424 try cg.addLocal(.local_set, result.local.value);
5425
5417 _ = try cg.isNull(lhs, operand_ty, .i32_eq);5426 _ = try cg.isNull(lhs, operand_ty, .i32_eq);
5427 try cg.addLocal(.local_tee, lhs_null.local.value);
5418 _ = try cg.isNull(rhs, operand_ty, .i32_eq);5428 _ = try cg.isNull(rhs, operand_ty, .i32_eq);
5419 try cg.addTag(.i32_ne); // inverse so we can exit early5429 try cg.addTag(.i32_ne);
5420 try cg.addLabel(.br_if, 0);5430 try cg.addLabel(.br_if, 0); // only one is null
5431
5432 try cg.addImm32(if (op == .eq) 1 else 0);
5433 try cg.addLocal(.local_set, result.local.value);
5434
5435 try cg.addLocal(.local_get, lhs_null.local.value);
5436 try cg.addLabel(.br_if, 0); // both are null
54215437
5422 _ = try cg.load(lhs, payload_ty, 0);5438 _ = try cg.load(lhs, payload_ty, 0);
5423 _ = try cg.load(rhs, payload_ty, 0);5439 _ = try cg.load(rhs, payload_ty, 0);
5424 const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, zcu, cg.target) });5440 _ = try cg.cmp(.stack, .stack, payload_ty, op);
5425 try cg.addTag(Mir.Inst.Tag.fromOpcode(opcode));
5426 try cg.addLabel(.br_if, 0);
5427
5428 try cg.addImm32(1);
5429 try cg.addLocal(.local_set, result.local.value);5441 try cg.addLocal(.local_set, result.local.value);
5442
5430 try cg.endBlock();5443 try cg.endBlock();
54315444
5432 try cg.emitWValue(result);5445 try cg.addLocal(.local_get, result.local.value);
5433 try cg.addImm32(0);5446
5434 try cg.addTag(if (op == .eq) .i32_ne else .i32_eq);
5435 return .stack;5447 return .stack;
5436}5448}
54375449
test/behavior/array.zig-2
...@@ -1049,7 +1049,6 @@ test "@splat array with sentinel" {...@@ -1049,7 +1049,6 @@ test "@splat array with sentinel" {
1049 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;1049 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1050 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1050 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1051 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;1051 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1052 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1053 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1052 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10541053
1055 const S = struct {1054 const S = struct {
...@@ -1074,7 +1073,6 @@ test "@splat zero-length array" {...@@ -1074,7 +1073,6 @@ test "@splat zero-length array" {
1074 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;1073 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1075 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1074 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1076 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;1075 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1077 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1078 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1076 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10791077
1080 const S = struct {1078 const S = struct {
test/behavior/optional.zig-3
...@@ -59,7 +59,6 @@ fn testNullPtrsEql() !void {...@@ -59,7 +59,6 @@ fn testNullPtrsEql() !void {
59}59}
6060
61test "optional with zero-bit type" {61test "optional with zero-bit type" {
62 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
63 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;62 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
64 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;63 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
6564
...@@ -212,7 +211,6 @@ test "equality compare optionals and non-optionals" {...@@ -212,7 +211,6 @@ test "equality compare optionals and non-optionals" {
212}211}
213212
214test "compare optionals with modified payloads" {213test "compare optionals with modified payloads" {
215 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
216 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;214 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
217215
218 var lhs: ?bool = false;216 var lhs: ?bool = false;
...@@ -643,7 +641,6 @@ test "result location initialization of optional with OPV payload" {...@@ -643,7 +641,6 @@ test "result location initialization of optional with OPV payload" {
643 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO641 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
644 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO642 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
645 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO643 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
646 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
647 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;644 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
648645
649 const S = struct {646 const S = struct {