authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-23 22:06:27+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-24 15:34:52-07:00
log3a059ebe4c84a1e541bb3b2ccee2e7cc25686a4d
tree5b1db05ab7e4b261ffa9372b536ca39cbf577f39
parentc90a97f9be9ffef858b0e450de5006f61a12fafd

wasm: Fixes for error union semantics


3 files changed, 110 insertions(+), 51 deletions(-)

src/arch/wasm/CodeGen.zig+107-47
......@@ -636,7 +636,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
636636 // means we must generate it from a constant.
637637 const val = self.air.value(ref).?;
638638 const ty = self.air.typeOf(ref);
639 if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isInt()) {
639 if (!ty.hasRuntimeBitsIgnoreComptime() and !ty.isInt() and !ty.isError()) {
640640 gop.value_ptr.* = WValue{ .none = {} };
641641 return gop.value_ptr.*;
642642 }
......@@ -804,6 +804,8 @@ fn genFunctype(gpa: Allocator, fn_info: Type.Payload.Function.Data, target: std.
804804 } else {
805805 try returns.append(typeToValtype(fn_info.return_type, target));
806806 }
807 } else if (fn_info.return_type.isError()) {
808 try returns.append(.i32);
807809 }
808810
809811 // param types
......@@ -1373,10 +1375,15 @@ fn isByRef(ty: Type, target: std.Target) bool {
13731375 .Int => return ty.intInfo(target).bits > 64,
13741376 .Float => return ty.floatBits(target) > 64,
13751377 .ErrorUnion => {
1376 const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime();
1377 const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime();
1378 if (!has_tag or !has_pl) return false;
1379 return ty.hasRuntimeBitsIgnoreComptime();
1378 const err_ty = ty.errorUnionSet();
1379 const pl_ty = ty.errorUnionPayload();
1380 if (err_ty.errorSetCardinality() == .zero) {
1381 return isByRef(pl_ty, target);
1382 }
1383 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
1384 return false;
1385 }
1386 return true;
13801387 },
13811388 .Optional => {
13821389 if (ty.isPtrLikeOptional()) return false;
......@@ -1624,13 +1631,14 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
16241631fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16251632 const un_op = self.air.instructions.items(.data)[inst].un_op;
16261633 const operand = try self.resolveInst(un_op);
1627 const ret_ty = self.decl.ty.fnReturnType();
1634 const fn_info = self.decl.ty.fnInfo();
1635 const ret_ty = fn_info.return_type;
16281636
16291637 // result must be stored in the stack and we return a pointer
16301638 // to the stack instead
16311639 if (self.return_value != .none) {
1632 try self.store(self.return_value, operand, self.decl.ty.fnReturnType(), 0);
1633 } else if (self.decl.ty.fnInfo().cc == .C and ret_ty.hasRuntimeBitsIgnoreComptime()) {
1640 try self.store(self.return_value, operand, ret_ty, 0);
1641 } else if (fn_info.cc == .C and ret_ty.hasRuntimeBitsIgnoreComptime()) {
16341642 switch (ret_ty.zigTypeTag()) {
16351643 // Aggregate types can be lowered as a singular value
16361644 .Struct, .Union => {
......@@ -1650,7 +1658,11 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16501658 else => try self.emitWValue(operand),
16511659 }
16521660 } else {
1653 try self.emitWValue(operand);
1661 if (!ret_ty.hasRuntimeBitsIgnoreComptime() and ret_ty.isError()) {
1662 try self.addImm32(0);
1663 } else {
1664 try self.emitWValue(operand);
1665 }
16541666 }
16551667 try self.restoreStackPointer();
16561668 try self.addTag(.@"return");
......@@ -1675,7 +1687,13 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16751687 const un_op = self.air.instructions.items(.data)[inst].un_op;
16761688 const operand = try self.resolveInst(un_op);
16771689 const ret_ty = self.air.typeOf(un_op).childType();
1678 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) return WValue.none;
1690 if (!ret_ty.hasRuntimeBitsIgnoreComptime()) {
1691 if (ret_ty.isError()) {
1692 try self.addImm32(0);
1693 } else {
1694 return WValue.none;
1695 }
1696 }
16791697
16801698 if (!firstParamSRet(self.decl.ty.fnInfo(), self.target)) {
16811699 const result = try self.load(operand, ret_ty, 0);
......@@ -1723,8 +1741,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
17231741
17241742 const sret = if (first_param_sret) blk: {
17251743 const sret_local = try self.allocStack(ret_ty);
1726 const ptr_offset = try self.buildPointerOffset(sret_local, 0, .new);
1727 try self.emitWValue(ptr_offset);
1744 try self.lowerToStack(sret_local);
17281745 break :blk sret_local;
17291746 } else WValue{ .none = {} };
17301747
......@@ -1754,7 +1771,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
17541771 try self.addLabel(.call_indirect, fn_type_index);
17551772 }
17561773
1757 if (self.liveness.isUnused(inst) or !ret_ty.hasRuntimeBitsIgnoreComptime()) {
1774 if (self.liveness.isUnused(inst) or (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError())) {
17581775 return WValue.none;
17591776 } else if (ret_ty.isNoReturn()) {
17601777 try self.addTag(.@"unreachable");
......@@ -1796,8 +1813,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
17961813 .ErrorUnion => {
17971814 const err_ty = ty.errorUnionSet();
17981815 const pl_ty = ty.errorUnionPayload();
1816 if (err_ty.errorSetCardinality() == .zero) {
1817 return self.store(lhs, rhs, pl_ty, 0);
1818 }
17991819 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
1800 return self.store(lhs, rhs, err_ty, 0);
1820 return self.store(lhs, rhs, Type.anyerror, 0);
18011821 }
18021822
18031823 const len = @intCast(u32, ty.abiSize(self.target));
......@@ -2256,6 +2276,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
22562276 const target = self.target;
22572277
22582278 switch (ty.zigTypeTag()) {
2279 .Void => return WValue{ .none = {} },
22592280 .Int => {
22602281 const int_info = ty.intInfo(self.target);
22612282 switch (int_info.signedness) {
......@@ -2324,6 +2345,10 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
23242345 },
23252346 .ErrorUnion => {
23262347 const error_type = ty.errorUnionSet();
2348 if (error_type.errorSetCardinality() == .zero) {
2349 const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
2350 return self.lowerConstant(pl_val, ty.errorUnionPayload());
2351 }
23272352 const is_pl = val.errorUnionIsPayload();
23282353 const err_val = if (!is_pl) val else Value.initTag(.zero);
23292354 return self.lowerConstant(err_val, error_type);
......@@ -2892,12 +2917,19 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
28922917 const err_ty = self.air.typeOf(un_op);
28932918 const pl_ty = err_ty.errorUnionPayload();
28942919
2895 // load the error tag value
2920 if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {
2921 switch (opcode) {
2922 .i32_ne => return WValue{ .imm32 = 0 },
2923 .i32_eq => return WValue{ .imm32 = 1 },
2924 else => unreachable,
2925 }
2926 }
2927
28962928 try self.emitWValue(operand);
28972929 if (pl_ty.hasRuntimeBitsIgnoreComptime()) {
28982930 try self.addMemArg(.i32_load16_u, .{
2899 .offset = operand.offset(),
2900 .alignment = err_ty.errorUnionSet().abiAlignment(self.target),
2931 .offset = operand.offset() + errUnionErrorOffset(pl_ty, self.target),
2932 .alignment = Type.anyerror.abiAlignment(self.target),
29012933 });
29022934 }
29032935
......@@ -2905,7 +2937,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
29052937 try self.addImm32(0);
29062938 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
29072939
2908 const is_err_tmp = try self.allocLocal(Type.initTag(.i32)); // result is always an i32
2940 const is_err_tmp = try self.allocLocal(Type.i32);
29092941 try self.addLabel(.local_set, is_err_tmp.local);
29102942 return is_err_tmp;
29112943}
......@@ -2917,14 +2949,18 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool)
29172949 const op_ty = self.air.typeOf(ty_op.operand);
29182950 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
29192951 const payload_ty = err_ty.errorUnionPayload();
2952
2953 if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {
2954 return operand;
2955 }
2956
29202957 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };
2921 const err_align = err_ty.abiAlignment(self.target);
2922 const set_size = err_ty.errorUnionSet().abiSize(self.target);
2923 const offset = mem.alignForwardGeneric(u64, set_size, err_align);
2958
2959 const pl_offset = errUnionPayloadOffset(payload_ty, self.target);
29242960 if (op_is_ptr or isByRef(payload_ty, self.target)) {
2925 return self.buildPointerOffset(operand, offset, .new);
2961 return self.buildPointerOffset(operand, pl_offset, .new);
29262962 }
2927 return self.load(operand, payload_ty, @intCast(u32, offset));
2963 return self.load(operand, payload_ty, pl_offset);
29282964}
29292965
29302966fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue {
......@@ -2935,11 +2971,16 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In
29352971 const op_ty = self.air.typeOf(ty_op.operand);
29362972 const err_ty = if (op_is_ptr) op_ty.childType() else op_ty;
29372973 const payload_ty = err_ty.errorUnionPayload();
2974
2975 if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {
2976 return WValue{ .imm32 = 0 };
2977 }
2978
29382979 if (op_is_ptr or !payload_ty.hasRuntimeBitsIgnoreComptime()) {
29392980 return operand;
29402981 }
29412982
2942 return self.load(operand, err_ty.errorUnionSet(), 0);
2983 return self.load(operand, Type.anyerror, errUnionErrorOffset(payload_ty, self.target));
29432984}
29442985
29452986fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2947,22 +2988,26 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29472988
29482989 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
29492990 const operand = try self.resolveInst(ty_op.operand);
2991 const err_ty = self.air.typeOfIndex(inst);
29502992
2951 const op_ty = self.air.typeOf(ty_op.operand);
2952 if (!op_ty.hasRuntimeBitsIgnoreComptime()) return operand;
2953 const err_union_ty = self.air.getRefType(ty_op.ty);
2954 const err_align = err_union_ty.abiAlignment(self.target);
2955 const set_size = err_union_ty.errorUnionSet().abiSize(self.target);
2956 const offset = mem.alignForwardGeneric(u64, set_size, err_align);
2993 if (err_ty.errorUnionSet().errorSetCardinality() == .zero) {
2994 return operand;
2995 }
2996
2997 const pl_ty = self.air.typeOf(ty_op.operand);
2998 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
2999 return operand;
3000 }
29573001
2958 const err_union = try self.allocStack(err_union_ty);
2959 const payload_ptr = try self.buildPointerOffset(err_union, offset, .new);
2960 try self.store(payload_ptr, operand, op_ty, 0);
3002 const err_union = try self.allocStack(err_ty);
3003 const payload_ptr = try self.buildPointerOffset(err_union, errUnionPayloadOffset(pl_ty, self.target), .new);
3004 try self.store(payload_ptr, operand, pl_ty, 0);
29613005
29623006 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
29633007 try self.emitWValue(err_union);
29643008 try self.addImm32(0);
2965 try self.addMemArg(.i32_store16, .{ .offset = err_union.offset(), .alignment = 2 });
3009 const err_val_offset = errUnionErrorOffset(pl_ty, self.target);
3010 try self.addMemArg(.i32_store16, .{ .offset = err_union.offset() + err_val_offset, .alignment = 2 });
29663011
29673012 return err_union;
29683013}
......@@ -2973,17 +3018,18 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29733018 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
29743019 const operand = try self.resolveInst(ty_op.operand);
29753020 const err_ty = self.air.getRefType(ty_op.ty);
3021 const pl_ty = err_ty.errorUnionPayload();
29763022
2977 if (!err_ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime()) return operand;
3023 if (!pl_ty.hasRuntimeBitsIgnoreComptime()) {
3024 return operand;
3025 }
29783026
29793027 const err_union = try self.allocStack(err_ty);
2980 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);
3028 // store error value
3029 try self.store(err_union, operand, Type.anyerror, errUnionErrorOffset(pl_ty, self.target));
29813030
29823031 // write 'undefined' to the payload
2983 const err_align = err_ty.abiAlignment(self.target);
2984 const set_size = err_ty.errorUnionSet().abiSize(self.target);
2985 const offset = mem.alignForwardGeneric(u64, set_size, err_align);
2986 const payload_ptr = try self.buildPointerOffset(err_union, offset, .new);
3032 const payload_ptr = try self.buildPointerOffset(err_union, errUnionPayloadOffset(pl_ty, self.target), .new);
29873033 const len = @intCast(u32, err_ty.errorUnionPayload().abiSize(self.target));
29883034 try self.memset(payload_ptr, .{ .imm32 = len }, .{ .imm32 = 0xaaaaaaaa });
29893035
......@@ -3927,12 +3973,16 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
39273973fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
39283974 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
39293975 const err_set_ty = self.air.typeOf(ty_op.operand).childType();
3930 const err_ty = err_set_ty.errorUnionSet();
39313976 const payload_ty = err_set_ty.errorUnionPayload();
39323977 const operand = try self.resolveInst(ty_op.operand);
39333978
39343979 // set error-tag to '0' to annotate error union is non-error
3935 try self.store(operand, .{ .imm32 = 0 }, err_ty, 0);
3980 try self.store(
3981 operand,
3982 .{ .imm32 = 0 },
3983 Type.anyerror,
3984 errUnionErrorOffset(payload_ty, self.target),
3985 );
39363986
39373987 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
39383988
......@@ -3940,11 +3990,7 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
39403990 return operand;
39413991 }
39423992
3943 const err_align = err_set_ty.abiAlignment(self.target);
3944 const set_size = err_ty.abiSize(self.target);
3945 const offset = mem.alignForwardGeneric(u64, set_size, err_align);
3946
3947 return self.buildPointerOffset(operand, @intCast(u32, offset), .new);
3993 return self.buildPointerOffset(operand, errUnionPayloadOffset(payload_ty, self.target), .new);
39483994}
39493995
39503996fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -4572,3 +4618,17 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue {
45724618 } });
45734619 return WValue{ .none = {} };
45744620}
4621
4622fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u32 {
4623 if (Type.anyerror.abiAlignment(target) > payload_ty.abiAlignment(target)) {
4624 return @intCast(u32, Type.anyerror.abiSize(target));
4625 }
4626 return 0;
4627}
4628
4629fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u32 {
4630 if (Type.anyerror.abiAlignment(target) > payload_ty.abiAlignment(target)) {
4631 return 0;
4632 }
4633 return @intCast(u32, payload_ty.abiSize(target));
4634}
src/codegen.zig+3-3
......@@ -714,7 +714,7 @@ pub fn generateSymbol(
714714 const is_payload = typed_value.val.errorUnionIsPayload();
715715
716716 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
717 const err_val = if (!is_payload) typed_value.val else Value.initTag(.zero);
717 const err_val = if (is_payload) Value.initTag(.zero) else typed_value.val;
718718 return generateSymbol(bin_file, src_loc, .{
719719 .ty = error_ty,
720720 .val = err_val,
......@@ -763,7 +763,7 @@ pub fn generateSymbol(
763763 }
764764
765765 // Payload size is larger than error set, so emit our error set last
766 if (error_align < payload_align) {
766 if (error_align <= payload_align) {
767767 const begin = code.items.len;
768768 switch (try generateSymbol(bin_file, src_loc, .{
769769 .ty = error_ty,
......@@ -794,7 +794,7 @@ pub fn generateSymbol(
794794 try code.writer().writeInt(u32, kv.value, endian);
795795 },
796796 else => {
797 try code.writer().writeByteNTimes(0, @intCast(usize, typed_value.ty.abiSize(target)));
797 try code.writer().writeByteNTimes(0, @intCast(usize, Type.anyerror.abiSize(target)));
798798 },
799799 }
800800 return Result{ .appended = {} };
test/behavior/error.zig-1
......@@ -260,7 +260,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
260260}
261261
262262test "comptime err to int of error set with only 1 possible value" {
263 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
264263 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
265264 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
266265 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO