authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-05-22 19:06:38+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-05-31 18:04:32+02:00
log49fddbf4c11d4ea493c1e5b6176052aeacc1ad10
tree57d3e00ec5c6f37196d74d8bf6caef7bc8765408
parent00dedabc41322bc2b4978ddc39ee17b72193f194
signaturelock-open Commit is signed but in an unrecognized format.

wasm: `union_init` correctly store the tag

Previously we would only store the payload, but not the actual tag that was set. This meant miscompilations where it would incorrectly return the tag value. This also adds a tiny optimization for payloads which are not `byRef` by directly storing them based on offset, rather than first calculating a pointer to an offset.

1 files changed, 33 insertions(+), 7 deletions(-)

src/arch/wasm/CodeGen.zig+33-7
...@@ -4982,26 +4982,52 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4982,26 +4982,52 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
4982 const result = result: {4982 const result = result: {
4983 const union_ty = func.air.typeOfIndex(inst);4983 const union_ty = func.air.typeOfIndex(inst);
4984 const layout = union_ty.unionGetLayout(func.target);4984 const layout = union_ty.unionGetLayout(func.target);
4985 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
4986 const field = union_obj.fields.values()[extra.field_index];
4987 const field_name = union_obj.fields.keys()[extra.field_index];
4988
4989 const tag_int = blk: {
4990 const tag_ty = union_ty.unionTagTypeHypothetical();
4991 const enum_field_index = tag_ty.enumFieldIndex(field_name).?;
4992 var tag_val_payload: Value.Payload.U32 = .{
4993 .base = .{ .tag = .enum_field_index },
4994 .data = @intCast(u32, enum_field_index),
4995 };
4996 const tag_val = Value.initPayload(&tag_val_payload.base);
4997 break :blk try func.lowerConstant(tag_val, tag_ty);
4998 };
4985 if (layout.payload_size == 0) {4999 if (layout.payload_size == 0) {
4986 if (layout.tag_size == 0) {5000 if (layout.tag_size == 0) {
4987 break :result WValue{ .none = {} };5001 break :result WValue{ .none = {} };
4988 }5002 }
4989 assert(!isByRef(union_ty, func.target));5003 assert(!isByRef(union_ty, func.target));
4990 break :result WValue{ .imm32 = extra.field_index };5004 break :result tag_int;
4991 }5005 }
4992 assert(isByRef(union_ty, func.target));5006 assert(isByRef(union_ty, func.target));
49935007
4994 const result_ptr = try func.allocStack(union_ty);5008 const result_ptr = try func.allocStack(union_ty);
4995 const payload = try func.resolveInst(extra.init);5009 const payload = try func.resolveInst(extra.init);
4996 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
4997 assert(union_obj.haveFieldTypes());
4998 const field = union_obj.fields.values()[extra.field_index];
4999
5000 if (layout.tag_align >= layout.payload_align) {5010 if (layout.tag_align >= layout.payload_align) {
5001 const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new);5011 if (isByRef(field.ty, func.target)) {
5002 try func.store(payload_ptr, payload, field.ty, 0);5012 const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new);
5013 try func.store(payload_ptr, payload, field.ty, 0);
5014 } else {
5015 try func.store(result_ptr, payload, field.ty, @intCast(u32, layout.tag_size));
5016 }
5017
5018 if (layout.tag_size > 0) {
5019 try func.store(result_ptr, tag_int, union_obj.tag_ty, 0);
5020 }
5003 } else {5021 } else {
5004 try func.store(result_ptr, payload, field.ty, 0);5022 try func.store(result_ptr, payload, field.ty, 0);
5023 if (layout.tag_size > 0) {
5024 try func.store(
5025 result_ptr,
5026 tag_int,
5027 union_obj.tag_ty,
5028 @intCast(u32, layout.payload_size),
5029 );
5030 }
5005 }5031 }
5006 break :result result_ptr;5032 break :result result_ptr;
5007 };5033 };