authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-05-25 17:52:39+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-05-31 18:04:32+02:00
log3c72b4d25eb14bbdc03503c368a750b1ca1b7c28
tree2caa5eccfc33ace3d70cca45d23b5996c58e90b6
parent7cfc44d86ff56fd760eaf0781cc3ba0650267af9
signaturelock-open Commit is signed but in an unrecognized format.

wasm: support and optimize for all packed unions

For packed unions where its abi size is less than or equal to 8 bytes we store it directly and don't pass it by reference. This means that when retrieving the field, we will perform shifts and bitcasts to ensure the correct type is returned. For larger packed unions, we either allocate a new stack value based on the field type when the field type is also passed by reference, or load it directly into a local if it's not.

1 files changed, 52 insertions(+), 3 deletions(-)

src/arch/wasm/CodeGen.zig+52-3
......@@ -1716,7 +1716,14 @@ fn isByRef(ty: Type, target: std.Target) bool {
17161716 .Array,
17171717 .Frame,
17181718 .Union,
1719 => return ty.hasRuntimeBitsIgnoreComptime(),
1719 => {
1720 if (ty.castTag(.@"union")) |union_ty| {
1721 if (union_ty.data.layout == .Packed) {
1722 return ty.abiSize(target) > 8;
1723 }
1724 }
1725 return ty.hasRuntimeBitsIgnoreComptime();
1726 },
17201727 .Struct => {
17211728 if (ty.castTag(.@"struct")) |struct_ty| {
17221729 const struct_obj = struct_ty.data;
......@@ -3131,6 +3138,14 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
31313138 val.writeToMemory(ty, func.bin_file.base.options.module.?, &buf) catch unreachable;
31323139 return func.storeSimdImmd(buf);
31333140 },
3141 .Union => {
3142 // in this case we have a packed union which will not be passed by reference.
3143 const union_ty = ty.cast(Type.Payload.Union).?.data;
3144 const union_obj = val.castTag(.@"union").?.data;
3145 const field_index = ty.unionTagFieldIndex(union_obj.tag, func.bin_file.base.options.module.?).?;
3146 const field_ty = union_ty.fields.values()[field_index].ty;
3147 return func.lowerConstant(union_obj.val, field_ty);
3148 },
31343149 else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),
31353150 }
31363151}
......@@ -3661,8 +3676,42 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
36613676 break :result try truncated.toLocal(func, field_ty);
36623677 },
36633678 .Union => result: {
3664 const val = try func.load(operand, field_ty, 0);
3665 break :result try val.toLocal(func, field_ty);
3679 if (isByRef(struct_ty, func.target)) {
3680 if (!isByRef(field_ty, func.target)) {
3681 const val = try func.load(operand, field_ty, 0);
3682 break :result try val.toLocal(func, field_ty);
3683 } else {
3684 const new_stack_val = try func.allocStack(field_ty);
3685 try func.store(new_stack_val, operand, field_ty, 0);
3686 break :result new_stack_val;
3687 }
3688 }
3689
3690 var payload: Type.Payload.Bits = .{
3691 .base = .{ .tag = .int_unsigned },
3692 .data = @intCast(u16, struct_ty.bitSize(func.target)),
3693 };
3694 const union_int_type = Type.initPayload(&payload.base);
3695 if (field_ty.zigTypeTag() == .Float) {
3696 var int_payload: Type.Payload.Bits = .{
3697 .base = .{ .tag = .int_unsigned },
3698 .data = @intCast(u16, field_ty.bitSize(func.target)),
3699 };
3700 const int_type = Type.initPayload(&int_payload.base);
3701 const truncated = try func.trunc(operand, int_type, union_int_type);
3702 const bitcasted = try func.bitcast(field_ty, int_type, truncated);
3703 break :result try bitcasted.toLocal(func, field_ty);
3704 } else if (field_ty.isPtrAtRuntime()) {
3705 var int_payload: Type.Payload.Bits = .{
3706 .base = .{ .tag = .int_unsigned },
3707 .data = @intCast(u16, field_ty.bitSize(func.target)),
3708 };
3709 const int_type = Type.initPayload(&int_payload.base);
3710 const truncated = try func.trunc(operand, int_type, union_int_type);
3711 break :result try truncated.toLocal(func, field_ty);
3712 }
3713 const truncated = try func.trunc(operand, field_ty, union_int_type);
3714 break :result try truncated.toLocal(func, field_ty);
36663715 },
36673716 else => unreachable,
36683717 },