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 {
49824982 const result = result: {
49834983 const union_ty = func.air.typeOfIndex(inst);
49844984 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 };
49854999 if (layout.payload_size == 0) {
49865000 if (layout.tag_size == 0) {
49875001 break :result WValue{ .none = {} };
49885002 }
49895003 assert(!isByRef(union_ty, func.target));
4990 break :result WValue{ .imm32 = extra.field_index };
5004 break :result tag_int;
49915005 }
49925006 assert(isByRef(union_ty, func.target));
49935007
49945008 const result_ptr = try func.allocStack(union_ty);
49955009 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
50005010 if (layout.tag_align >= layout.payload_align) {
5001 const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new);
5002 try func.store(payload_ptr, payload, field.ty, 0);
5011 if (isByRef(field.ty, func.target)) {
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 }
50035021 } else {
50045022 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 }
50055031 }
50065032 break :result result_ptr;
50075033 };