authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-11-23 19:36:23+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-11-30 17:56:01+01:00
logeb2caf939023bba16e473661d4f3cd1c19616e53
treec7c5fc7da6b90d3baf394ef8022a180369f60597
parent7cf442cabcb08b84b21ebc3850f52ac796093ecb
signaturelock-open Commit is signed but in an unrecognized format.

wasm: airStructFieldVal - Support packed structs

This implements loading a field from a packed struct, regardless of its field's type. This means it supports pointers, floats and integers. The commit also extracts the logic from airTrunc into its own `trunc` function so it can be re-used.

1 files changed, 70 insertions(+), 23 deletions(-)

src/arch/wasm/CodeGen.zig+70-23
......@@ -909,6 +909,13 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
909909 if (info.bits > 32 and info.bits <= 128) break :blk wasm.Valtype.i64;
910910 break :blk wasm.Valtype.i32; // represented as pointer to stack
911911 },
912 .Struct => switch (ty.containerLayout()) {
913 .Packed => {
914 const struct_obj = ty.castTag(.@"struct").?.data;
915 return typeToValtype(struct_obj.backing_int_ty, target);
916 },
917 else => wasm.Valtype.i32,
918 },
912919 else => wasm.Valtype.i32, // all represented as reference/immediate
913920 };
914921}
......@@ -2415,7 +2422,7 @@ fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr
24152422/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack.
24162423fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
24172424 assert(ty.abiSize(func.target) <= 16);
2418 const bitsize = ty.intInfo(func.target).bits;
2425 const bitsize = @intCast(u16, ty.bitSize(func.target));
24192426 const wasm_bits = toWasmBits(bitsize) orelse {
24202427 return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
24212428 };
......@@ -3170,23 +3177,57 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
31703177 const field_ty = struct_ty.structFieldType(field_index);
31713178 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{struct_field.struct_operand});
31723179
3173 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, func.target)) orelse {
3174 const module = func.bin_file.base.options.module.?;
3175 return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});
3176 };
3177
3178 const result = result: {
3179 if (isByRef(field_ty, func.target)) {
3180 switch (operand) {
3181 .stack_offset => |stack_offset| {
3182 break :result WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
3183 },
3184 else => break :result try func.buildPointerOffset(operand, offset, .new),
3180 const result = switch (struct_ty.containerLayout()) {
3181 .Packed => switch (struct_ty.zigTypeTag()) {
3182 .Struct => result: {
3183 const struct_obj = struct_ty.castTag(.@"struct").?.data;
3184 assert(struct_obj.layout == .Packed);
3185 const offset = struct_obj.packedFieldBitOffset(func.target, field_index);
3186 const backing_ty = struct_obj.backing_int_ty;
3187 const wasm_bits = toWasmBits(backing_ty.intInfo(func.target).bits).?;
3188 const const_wvalue = if (wasm_bits == 32)
3189 WValue{ .imm32 = offset }
3190 else
3191 WValue{ .imm64 = offset };
3192
3193 // for first field we don't require any shifting
3194 const shifted_value = if (offset == 0)
3195 operand
3196 else
3197 try func.binOp(operand, const_wvalue, backing_ty, .shr);
3198
3199 if (field_ty.zigTypeTag() == .Float) {
3200 var payload: Type.Payload.Bits = .{
3201 .base = .{ .tag = .int_unsigned },
3202 .data = @intCast(u16, field_ty.bitSize(func.target)),
3203 };
3204 const int_type = Type.initPayload(&payload.base);
3205 const truncated = try func.trunc(shifted_value, int_type, backing_ty);
3206 const bitcasted = try func.bitcast(field_ty, int_type, truncated);
3207 break :result try bitcasted.toLocal(func, field_ty);
3208 }
3209 const truncated = try func.trunc(shifted_value, field_ty, backing_ty);
3210 break :result try truncated.toLocal(func, field_ty);
3211 },
3212 .Union => return func.fail("TODO: airStructFieldVal for packed unions", .{}),
3213 else => unreachable,
3214 },
3215 else => result: {
3216 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, func.target)) orelse {
3217 const module = func.bin_file.base.options.module.?;
3218 return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});
3219 };
3220 if (isByRef(field_ty, func.target)) {
3221 switch (operand) {
3222 .stack_offset => |stack_offset| {
3223 break :result WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };
3224 },
3225 else => break :result try func.buildPointerOffset(operand, offset, .new),
3226 }
31853227 }
3186 }
3187
3188 const field = try func.load(operand, field_ty, offset);
3189 break :result try field.toLocal(func, field_ty);
3228 const field = try func.load(operand, field_ty, offset);
3229 break :result try field.toLocal(func, field_ty);
3230 },
31903231 };
31913232 func.finishAir(inst, result, &.{struct_field.struct_operand});
31923233}
......@@ -3819,19 +3860,25 @@ fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
38193860 const wanted_ty = func.air.getRefType(ty_op.ty);
38203861 const op_ty = func.air.typeOf(ty_op.operand);
38213862
3822 const int_info = op_ty.intInfo(func.target);
3863 const result = try func.trunc(operand, wanted_ty, op_ty);
3864 func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand});
3865}
3866
3867/// Truncates a given operand to a given type, discarding any overflown bits.
3868/// NOTE: Resulting value is left on the stack.
3869fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue {
3870 const int_info = given_ty.intInfo(func.target);
38233871 if (toWasmBits(int_info.bits) == null) {
38243872 return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits});
38253873 }
38263874
3827 var result = try func.intcast(operand, op_ty, wanted_ty);
3875 var result = try func.intcast(operand, given_ty, wanted_ty);
38283876 const wanted_bits = wanted_ty.intInfo(func.target).bits;
38293877 const wasm_bits = toWasmBits(wanted_bits).?;
38303878 if (wasm_bits != wanted_bits) {
38313879 result = try func.wrapOperand(result, wanted_ty);
38323880 }
3833
3834 func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand});
3881 return result;
38353882}
38363883
38373884fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -4486,8 +4533,8 @@ fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
44864533
44874534 const dest_ty = func.air.typeOfIndex(inst);
44884535 const operand = try func.resolveInst(ty_op.operand);
4489 const trunc = try func.fptrunc(operand, func.air.typeOf(ty_op.operand), dest_ty);
4490 const result = try trunc.toLocal(func, dest_ty);
4536 const truncated = try func.fptrunc(operand, func.air.typeOf(ty_op.operand), dest_ty);
4537 const result = try truncated.toLocal(func, dest_ty);
44914538 func.finishAir(inst, result, &.{ty_op.operand});
44924539}
44934540