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 {...@@ -909,6 +909,13 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype {
909 if (info.bits > 32 and info.bits <= 128) break :blk wasm.Valtype.i64;909 if (info.bits > 32 and info.bits <= 128) break :blk wasm.Valtype.i64;
910 break :blk wasm.Valtype.i32; // represented as pointer to stack910 break :blk wasm.Valtype.i32; // represented as pointer to stack
911 },911 },
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 },
912 else => wasm.Valtype.i32, // all represented as reference/immediate919 else => wasm.Valtype.i32, // all represented as reference/immediate
913 };920 };
914}921}
...@@ -2415,7 +2422,7 @@ fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr...@@ -2415,7 +2422,7 @@ fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr
2415/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack.2422/// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack.
2416fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {2423fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
2417 assert(ty.abiSize(func.target) <= 16);2424 assert(ty.abiSize(func.target) <= 16);
2418 const bitsize = ty.intInfo(func.target).bits;2425 const bitsize = @intCast(u16, ty.bitSize(func.target));
2419 const wasm_bits = toWasmBits(bitsize) orelse {2426 const wasm_bits = toWasmBits(bitsize) orelse {
2420 return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});2427 return func.fail("TODO: Implement wrapOperand for bitsize '{d}'", .{bitsize});
2421 };2428 };
...@@ -3170,23 +3177,57 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3170,23 +3177,57 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3170 const field_ty = struct_ty.structFieldType(field_index);3177 const field_ty = struct_ty.structFieldType(field_index);
3171 if (!field_ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{struct_field.struct_operand});3178 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 {3180 const result = switch (struct_ty.containerLayout()) {
3174 const module = func.bin_file.base.options.module.?;3181 .Packed => switch (struct_ty.zigTypeTag()) {
3175 return func.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});3182 .Struct => result: {
3176 };3183 const struct_obj = struct_ty.castTag(.@"struct").?.data;
31773184 assert(struct_obj.layout == .Packed);
3178 const result = result: {3185 const offset = struct_obj.packedFieldBitOffset(func.target, field_index);
3179 if (isByRef(field_ty, func.target)) {3186 const backing_ty = struct_obj.backing_int_ty;
3180 switch (operand) {3187 const wasm_bits = toWasmBits(backing_ty.intInfo(func.target).bits).?;
3181 .stack_offset => |stack_offset| {3188 const const_wvalue = if (wasm_bits == 32)
3182 break :result WValue{ .stack_offset = .{ .value = stack_offset.value + offset, .references = 1 } };3189 WValue{ .imm32 = offset }
3183 },3190 else
3184 else => break :result try func.buildPointerOffset(operand, offset, .new),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 }
3185 }3227 }
3186 }3228 const field = try func.load(operand, field_ty, offset);
31873229 break :result try field.toLocal(func, field_ty);
3188 const field = try func.load(operand, field_ty, offset);3230 },
3189 break :result try field.toLocal(func, field_ty);
3190 };3231 };
3191 func.finishAir(inst, result, &.{struct_field.struct_operand});3232 func.finishAir(inst, result, &.{struct_field.struct_operand});
3192}3233}
...@@ -3819,19 +3860,25 @@ fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3819,19 +3860,25 @@ fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3819 const wanted_ty = func.air.getRefType(ty_op.ty);3860 const wanted_ty = func.air.getRefType(ty_op.ty);
3820 const op_ty = func.air.typeOf(ty_op.operand);3861 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);
3823 if (toWasmBits(int_info.bits) == null) {3871 if (toWasmBits(int_info.bits) == null) {
3824 return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits});3872 return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits});
3825 }3873 }
38263874
3827 var result = try func.intcast(operand, op_ty, wanted_ty);3875 var result = try func.intcast(operand, given_ty, wanted_ty);
3828 const wanted_bits = wanted_ty.intInfo(func.target).bits;3876 const wanted_bits = wanted_ty.intInfo(func.target).bits;
3829 const wasm_bits = toWasmBits(wanted_bits).?;3877 const wasm_bits = toWasmBits(wanted_bits).?;
3830 if (wasm_bits != wanted_bits) {3878 if (wasm_bits != wanted_bits) {
3831 result = try func.wrapOperand(result, wanted_ty);3879 result = try func.wrapOperand(result, wanted_ty);
3832 }3880 }
38333881 return result;
3834 func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand});
3835}3882}
38363883
3837fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3884fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
...@@ -4486,8 +4533,8 @@ fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4486,8 +4533,8 @@ fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
44864533
4487 const dest_ty = func.air.typeOfIndex(inst);4534 const dest_ty = func.air.typeOfIndex(inst);
4488 const operand = try func.resolveInst(ty_op.operand);4535 const operand = try func.resolveInst(ty_op.operand);
4489 const trunc = try func.fptrunc(operand, func.air.typeOf(ty_op.operand), dest_ty);4536 const truncated = try func.fptrunc(operand, func.air.typeOf(ty_op.operand), dest_ty);
4490 const result = try trunc.toLocal(func, dest_ty);4537 const result = try truncated.toLocal(func, dest_ty);
4491 func.finishAir(inst, result, &.{ty_op.operand});4538 func.finishAir(inst, result, &.{ty_op.operand});
4492}4539}
44934540