| ... | ... | @@ -909,6 +909,13 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype { |
| 909 | 909 | if (info.bits > 32 and info.bits <= 128) break :blk wasm.Valtype.i64; |
| 910 | 910 | 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 | 919 | 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 | 2422 | /// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack. |
| 2416 | 2423 | fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { |
| 2417 | 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 | 2426 | const wasm_bits = toWasmBits(bitsize) orelse { |
| 2420 | 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 | 3177 | const field_ty = struct_ty.structFieldType(field_index); |
| 3171 | 3178 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) return func.finishAir(inst, .none, &.{struct_field.struct_operand}); |
| 3172 | 3179 | |
| 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 | } |
| 3185 | 3227 | } |
| 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 | }, |
| 3190 | 3231 | }; |
| 3191 | 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 | 3860 | const wanted_ty = func.air.getRefType(ty_op.ty); |
| 3820 | 3861 | const op_ty = func.air.typeOf(ty_op.operand); |
| 3821 | 3862 | |
| 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. |
| 3869 | fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue { |
| 3870 | const int_info = given_ty.intInfo(func.target); |
| 3823 | 3871 | if (toWasmBits(int_info.bits) == null) { |
| 3824 | 3872 | return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{int_info.bits}); |
| 3825 | 3873 | } |
| 3826 | 3874 | |
| 3827 | | var result = try func.intcast(operand, op_ty, wanted_ty); |
| 3875 | var result = try func.intcast(operand, given_ty, wanted_ty); |
| 3828 | 3876 | const wanted_bits = wanted_ty.intInfo(func.target).bits; |
| 3829 | 3877 | const wasm_bits = toWasmBits(wanted_bits).?; |
| 3830 | 3878 | if (wasm_bits != wanted_bits) { |
| 3831 | 3879 | result = try func.wrapOperand(result, wanted_ty); |
| 3832 | 3880 | } |
| 3833 | | |
| 3834 | | func.finishAir(inst, try result.toLocal(func, wanted_ty), &.{ty_op.operand}); |
| 3881 | return result; |
| 3835 | 3882 | } |
| 3836 | 3883 | |
| 3837 | 3884 | fn airBoolToInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -4486,8 +4533,8 @@ fn airFptrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4486 | 4533 | |
| 4487 | 4534 | const dest_ty = func.air.typeOfIndex(inst); |
| 4488 | 4535 | 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); |
| 4491 | 4538 | func.finishAir(inst, result, &.{ty_op.operand}); |
| 4492 | 4539 | } |
| 4493 | 4540 | |