authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2025-07-23 20:52:26+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2025-07-24 01:18:02+02:00
logfcd9f521d2922f988786833db9f00a28757d418b
tree7303e11999984e1759f1287c02c21c4426bc45ef
parentbc8e1a74c514e487842c03ab32d7f6e49f42c529

stage2-wasm: implement try_ptr + is_(non_)err_ptr


2 files changed, 88 insertions(+), 12 deletions(-)

src/arch/wasm/CodeGen.zig+9-12
...@@ -1886,8 +1886,10 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1886,8 +1886,10 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1886 .call_never_tail => cg.airCall(inst, .never_tail),1886 .call_never_tail => cg.airCall(inst, .never_tail),
1887 .call_never_inline => cg.airCall(inst, .never_inline),1887 .call_never_inline => cg.airCall(inst, .never_inline),
18881888
1889 .is_err => cg.airIsErr(inst, .i32_ne),1889 .is_err => cg.airIsErr(inst, .i32_ne, .value),
1890 .is_non_err => cg.airIsErr(inst, .i32_eq),1890 .is_non_err => cg.airIsErr(inst, .i32_eq, .value),
1891 .is_err_ptr => cg.airIsErr(inst, .i32_ne, .ptr),
1892 .is_non_err_ptr => cg.airIsErr(inst, .i32_eq, .ptr),
18911893
1892 .is_null => cg.airIsNull(inst, .i32_eq, .value),1894 .is_null => cg.airIsNull(inst, .i32_eq, .value),
1893 .is_non_null => cg.airIsNull(inst, .i32_ne, .value),1895 .is_non_null => cg.airIsNull(inst, .i32_ne, .value),
...@@ -1970,8 +1972,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1970,8 +1972,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1970 .runtime_nav_ptr => cg.airRuntimeNavPtr(inst),1972 .runtime_nav_ptr => cg.airRuntimeNavPtr(inst),
19711973
1972 .assembly,1974 .assembly,
1973 .is_err_ptr,
1974 .is_non_err_ptr,
19751975
1976 .err_return_trace,1976 .err_return_trace,
1977 .set_err_return_trace,1977 .set_err_return_trace,
...@@ -4105,7 +4105,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4105,7 +4105,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4105 return cg.finishAir(inst, .none, &.{br.operand});4105 return cg.finishAir(inst, .none, &.{br.operand});
4106}4106}
41074107
4108fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void {4108fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
4109 const zcu = cg.pt.zcu;4109 const zcu = cg.pt.zcu;
4110 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4110 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
4111 const operand = try cg.resolveInst(un_op);4111 const operand = try cg.resolveInst(un_op);
...@@ -4122,7 +4122,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerEr...@@ -4122,7 +4122,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerEr
4122 }4122 }
41234123
4124 try cg.emitWValue(operand);4124 try cg.emitWValue(operand);
4125 if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {4125 if (op_kind == .ptr or pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4126 try cg.addMemArg(.i32_load16_u, .{4126 try cg.addMemArg(.i32_load16_u, .{
4127 .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))),4127 .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))),
4128 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),4128 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
...@@ -6462,9 +6462,6 @@ fn lowerTry(...@@ -6462,9 +6462,6 @@ fn lowerTry(
6462 operand_is_ptr: bool,6462 operand_is_ptr: bool,
6463) InnerError!WValue {6463) InnerError!WValue {
6464 const zcu = cg.pt.zcu;6464 const zcu = cg.pt.zcu;
6465 if (operand_is_ptr) {
6466 return cg.fail("TODO: lowerTry for pointers", .{});
6467 }
64686465
6469 const pl_ty = err_union_ty.errorUnionPayload(zcu);6466 const pl_ty = err_union_ty.errorUnionPayload(zcu);
6470 const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu);6467 const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu);
...@@ -6475,7 +6472,7 @@ fn lowerTry(...@@ -6475,7 +6472,7 @@ fn lowerTry(
64756472
6476 // check if the error tag is set for the error union.6473 // check if the error tag is set for the error union.
6477 try cg.emitWValue(err_union);6474 try cg.emitWValue(err_union);
6478 if (pl_has_bits) {6475 if (pl_has_bits or operand_is_ptr) {
6479 const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));6476 const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
6480 try cg.addMemArg(.i32_load16_u, .{6477 try cg.addMemArg(.i32_load16_u, .{
6481 .offset = err_union.offset() + err_offset,6478 .offset = err_union.offset() + err_offset,
...@@ -6497,12 +6494,12 @@ fn lowerTry(...@@ -6497,12 +6494,12 @@ fn lowerTry(
6497 }6494 }
64986495
6499 // if we reach here it means error was not set, and we want the payload6496 // if we reach here it means error was not set, and we want the payload
6500 if (!pl_has_bits) {6497 if (!pl_has_bits and !operand_is_ptr) {
6501 return .none;6498 return .none;
6502 }6499 }
65036500
6504 const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));6501 const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
6505 if (isByRef(pl_ty, zcu, cg.target)) {6502 if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) {
6506 return buildPointerOffset(cg, err_union, pl_offset, .new);6503 return buildPointerOffset(cg, err_union, pl_offset, .new);
6507 }6504 }
6508 const payload = try cg.load(err_union, pl_ty, pl_offset);6505 const payload = try cg.load(err_union, pl_ty, pl_offset);
test/behavior/try.zig+79
...@@ -121,3 +121,82 @@ test "'return try' through conditional" {...@@ -121,3 +121,82 @@ test "'return try' through conditional" {
121 comptime std.debug.assert(result == 123);121 comptime std.debug.assert(result == 123);
122 }122 }
123}123}
124
125test "try ptr propagation const" {
126 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
127 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
128 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
129 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
130 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
131
132 const S = struct {
133 fn foo0() !u32 {
134 return 0;
135 }
136
137 fn foo1() error{Bad}!u32 {
138 return 1;
139 }
140
141 fn foo2() anyerror!u32 {
142 return 2;
143 }
144
145 fn doTheTest() !void {
146 const res0: *const u32 = &(try foo0());
147 const res1: *const u32 = &(try foo1());
148 const res2: *const u32 = &(try foo2());
149 try expect(res0.* == 0);
150 try expect(res1.* == 1);
151 try expect(res2.* == 2);
152 }
153 };
154 try S.doTheTest();
155 try comptime S.doTheTest();
156}
157
158test "try ptr propagation mutate" {
159 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
160 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
161 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
162 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
163 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
164
165 const S = struct {
166 fn foo0() !u32 {
167 return 0;
168 }
169
170 fn foo1() error{Bad}!u32 {
171 return 1;
172 }
173
174 fn foo2() anyerror!u32 {
175 return 2;
176 }
177
178 fn doTheTest() !void {
179 var f0 = foo0();
180 var f1 = foo1();
181 var f2 = foo2();
182
183 const res0: *u32 = &(try f0);
184 const res1: *u32 = &(try f1);
185 const res2: *u32 = &(try f2);
186
187 res0.* += 1;
188 res1.* += 1;
189 res2.* += 1;
190
191 try expect(f0 catch unreachable == 1);
192 try expect(f1 catch unreachable == 2);
193 try expect(f2 catch unreachable == 3);
194
195 try expect(res0.* == 1);
196 try expect(res1.* == 2);
197 try expect(res2.* == 3);
198 }
199 };
200 try S.doTheTest();
201 try comptime S.doTheTest();
202}