diff options
| author | Luuk de Gram <luuk@degram.dev> | 2023-05-25 17:52:39 +0200 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2023-05-31 18:04:32 +0200 |
| commit | 3c72b4d25eb14bbdc03503c368a750b1ca1b7c28 (patch) | |
| tree | 2caa5eccfc33ace3d70cca45d23b5996c58e90b6 /src | |
| parent | 7cfc44d86ff56fd760eaf0781cc3ba0650267af9 (diff) | |
| download | zig-3c72b4d25eb14bbdc03503c368a750b1ca1b7c28.tar.gz zig-3c72b4d25eb14bbdc03503c368a750b1ca1b7c28.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 55 |
1 files changed, 52 insertions, 3 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 4514311160..74eb075eab 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1716,7 +1716,14 @@ fn isByRef(ty: Type, target: std.Target) bool { .Array, .Frame, .Union, - => return ty.hasRuntimeBitsIgnoreComptime(), + => { + if (ty.castTag(.@"union")) |union_ty| { + if (union_ty.data.layout == .Packed) { + return ty.abiSize(target) > 8; + } + } + return ty.hasRuntimeBitsIgnoreComptime(); + }, .Struct => { if (ty.castTag(.@"struct")) |struct_ty| { const struct_obj = struct_ty.data; @@ -3131,6 +3138,14 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { val.writeToMemory(ty, func.bin_file.base.options.module.?, &buf) catch unreachable; return func.storeSimdImmd(buf); }, + .Union => { + // in this case we have a packed union which will not be passed by reference. + const union_ty = ty.cast(Type.Payload.Union).?.data; + const union_obj = val.castTag(.@"union").?.data; + const field_index = ty.unionTagFieldIndex(union_obj.tag, func.bin_file.base.options.module.?).?; + const field_ty = union_ty.fields.values()[field_index].ty; + return func.lowerConstant(union_obj.val, field_ty); + }, else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}), } } @@ -3661,8 +3676,42 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { break :result try truncated.toLocal(func, field_ty); }, .Union => result: { - const val = try func.load(operand, field_ty, 0); - break :result try val.toLocal(func, field_ty); + if (isByRef(struct_ty, func.target)) { + if (!isByRef(field_ty, func.target)) { + const val = try func.load(operand, field_ty, 0); + break :result try val.toLocal(func, field_ty); + } else { + const new_stack_val = try func.allocStack(field_ty); + try func.store(new_stack_val, operand, field_ty, 0); + break :result new_stack_val; + } + } + + var payload: Type.Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = @intCast(u16, struct_ty.bitSize(func.target)), + }; + const union_int_type = Type.initPayload(&payload.base); + if (field_ty.zigTypeTag() == .Float) { + var int_payload: Type.Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = @intCast(u16, field_ty.bitSize(func.target)), + }; + const int_type = Type.initPayload(&int_payload.base); + const truncated = try func.trunc(operand, int_type, union_int_type); + const bitcasted = try func.bitcast(field_ty, int_type, truncated); + break :result try bitcasted.toLocal(func, field_ty); + } else if (field_ty.isPtrAtRuntime()) { + var int_payload: Type.Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = @intCast(u16, field_ty.bitSize(func.target)), + }; + const int_type = Type.initPayload(&int_payload.base); + const truncated = try func.trunc(operand, int_type, union_int_type); + break :result try truncated.toLocal(func, field_ty); + } + const truncated = try func.trunc(operand, field_ty, union_int_type); + break :result try truncated.toLocal(func, field_ty); }, else => unreachable, }, |
