From fcd9f521d2922f988786833db9f00a28757d418b Mon Sep 17 00:00:00 2001 From: Pavel Verigo Date: Wed, 23 Jul 2025 20:52:26 +0200 Subject: [PATCH] stage2-wasm: implement try_ptr + is_(non_)err_ptr --- src/arch/wasm/CodeGen.zig | 21 +++++------ test/behavior/try.zig | 79 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 12 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index fca32627b9..e56fb1df96 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1886,8 +1886,10 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .call_never_tail => cg.airCall(inst, .never_tail), .call_never_inline => cg.airCall(inst, .never_inline), - .is_err => cg.airIsErr(inst, .i32_ne), - .is_non_err => cg.airIsErr(inst, .i32_eq), + .is_err => cg.airIsErr(inst, .i32_ne, .value), + .is_non_err => cg.airIsErr(inst, .i32_eq, .value), + .is_err_ptr => cg.airIsErr(inst, .i32_ne, .ptr), + .is_non_err_ptr => cg.airIsErr(inst, .i32_eq, .ptr), .is_null => cg.airIsNull(inst, .i32_eq, .value), .is_non_null => cg.airIsNull(inst, .i32_ne, .value), @@ -1970,8 +1972,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .runtime_nav_ptr => cg.airRuntimeNavPtr(inst), .assembly, - .is_err_ptr, - .is_non_err_ptr, .err_return_trace, .set_err_return_trace, @@ -4105,7 +4105,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { return cg.finishAir(inst, .none, &.{br.operand}); } -fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void { +fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void { const zcu = cg.pt.zcu; const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try cg.resolveInst(un_op); @@ -4122,7 +4122,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerEr } try cg.emitWValue(operand); - if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (op_kind == .ptr or pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { try cg.addMemArg(.i32_load16_u, .{ .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))), .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), @@ -6462,9 +6462,6 @@ fn lowerTry( operand_is_ptr: bool, ) InnerError!WValue { const zcu = cg.pt.zcu; - if (operand_is_ptr) { - return cg.fail("TODO: lowerTry for pointers", .{}); - } const pl_ty = err_union_ty.errorUnionPayload(zcu); const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu); @@ -6475,7 +6472,7 @@ fn lowerTry( // check if the error tag is set for the error union. try cg.emitWValue(err_union); - if (pl_has_bits) { + if (pl_has_bits or operand_is_ptr) { const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); try cg.addMemArg(.i32_load16_u, .{ .offset = err_union.offset() + err_offset, @@ -6497,12 +6494,12 @@ fn lowerTry( } // if we reach here it means error was not set, and we want the payload - if (!pl_has_bits) { + if (!pl_has_bits and !operand_is_ptr) { return .none; } const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); - if (isByRef(pl_ty, zcu, cg.target)) { + if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) { return buildPointerOffset(cg, err_union, pl_offset, .new); } const payload = try cg.load(err_union, pl_ty, pl_offset); diff --git a/test/behavior/try.zig b/test/behavior/try.zig index dd9885868f..b3014ef669 100644 --- a/test/behavior/try.zig +++ b/test/behavior/try.zig @@ -121,3 +121,82 @@ test "'return try' through conditional" { comptime std.debug.assert(result == 123); } } + +test "try ptr propagation const" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const S = struct { + fn foo0() !u32 { + return 0; + } + + fn foo1() error{Bad}!u32 { + return 1; + } + + fn foo2() anyerror!u32 { + return 2; + } + + fn doTheTest() !void { + const res0: *const u32 = &(try foo0()); + const res1: *const u32 = &(try foo1()); + const res2: *const u32 = &(try foo2()); + try expect(res0.* == 0); + try expect(res1.* == 1); + try expect(res2.* == 2); + } + }; + try S.doTheTest(); + try comptime S.doTheTest(); +} + +test "try ptr propagation mutate" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const S = struct { + fn foo0() !u32 { + return 0; + } + + fn foo1() error{Bad}!u32 { + return 1; + } + + fn foo2() anyerror!u32 { + return 2; + } + + fn doTheTest() !void { + var f0 = foo0(); + var f1 = foo1(); + var f2 = foo2(); + + const res0: *u32 = &(try f0); + const res1: *u32 = &(try f1); + const res2: *u32 = &(try f2); + + res0.* += 1; + res1.* += 1; + res2.* += 1; + + try expect(f0 catch unreachable == 1); + try expect(f1 catch unreachable == 2); + try expect(f2 catch unreachable == 3); + + try expect(res0.* == 1); + try expect(res1.* == 2); + try expect(res2.* == 3); + } + }; + try S.doTheTest(); + try comptime S.doTheTest(); +}