zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 49fddbf4c11d4ea493c1e5b6176052aeacc1ad10 (tree)
parent 00dedabc41322bc2b4978ddc39ee17b72193f194
Author: Luuk de Gram <luuk@degram.dev>
Date:   Mon, 22 May 2023 19:06:38 +0200

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.

Diffstat:
Msrc/arch/wasm/CodeGen.zig | 40+++++++++++++++++++++++++++++++++-------
1 file changed, 33 insertions(+), 7 deletions(-)

diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig @@ -4982,26 +4982,52 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const result = result: { const union_ty = func.air.typeOfIndex(inst); const layout = union_ty.unionGetLayout(func.target); + const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const field = union_obj.fields.values()[extra.field_index]; + const field_name = union_obj.fields.keys()[extra.field_index]; + + const tag_int = blk: { + const tag_ty = union_ty.unionTagTypeHypothetical(); + const enum_field_index = tag_ty.enumFieldIndex(field_name).?; + var tag_val_payload: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, enum_field_index), + }; + const tag_val = Value.initPayload(&tag_val_payload.base); + break :blk try func.lowerConstant(tag_val, tag_ty); + }; if (layout.payload_size == 0) { if (layout.tag_size == 0) { break :result WValue{ .none = {} }; } assert(!isByRef(union_ty, func.target)); - break :result WValue{ .imm32 = extra.field_index }; + break :result tag_int; } assert(isByRef(union_ty, func.target)); const result_ptr = try func.allocStack(union_ty); const payload = try func.resolveInst(extra.init); - const union_obj = union_ty.cast(Type.Payload.Union).?.data; - assert(union_obj.haveFieldTypes()); - const field = union_obj.fields.values()[extra.field_index]; - if (layout.tag_align >= layout.payload_align) { - const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new); - try func.store(payload_ptr, payload, field.ty, 0); + if (isByRef(field.ty, func.target)) { + const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new); + try func.store(payload_ptr, payload, field.ty, 0); + } else { + try func.store(result_ptr, payload, field.ty, @intCast(u32, layout.tag_size)); + } + + if (layout.tag_size > 0) { + try func.store(result_ptr, tag_int, union_obj.tag_ty, 0); + } } else { try func.store(result_ptr, payload, field.ty, 0); + if (layout.tag_size > 0) { + try func.store( + result_ptr, + tag_int, + union_obj.tag_ty, + @intCast(u32, layout.payload_size), + ); + } } break :result result_ptr; };