| ... | ... | @@ -1818,11 +1818,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1818 | 1818 | try self.emitWValue(rhs); |
| 1819 | 1819 | } |
| 1820 | 1820 | const valtype = typeToValtype(ty, self.target); |
| 1821 | | const abi_size = @intCast(u8, ty.bitSize(self.target)); |
| 1821 | const abi_size = @intCast(u8, ty.abiSize(self.target)); |
| 1822 | 1822 | |
| 1823 | 1823 | const opcode = buildOpcode(.{ |
| 1824 | 1824 | .valtype1 = valtype, |
| 1825 | | .width = abi_size * 8, // use bitsize instead of byte size |
| 1825 | .width = abi_size * 8, |
| 1826 | 1826 | .op = .store, |
| 1827 | 1827 | }); |
| 1828 | 1828 | |
| ... | ... | @@ -1853,10 +1853,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1853 | 1853 | // load local's value from memory by its stack position |
| 1854 | 1854 | try self.emitWValue(operand); |
| 1855 | 1855 | |
| 1856 | | const abi_size = @intCast(u8, ty.bitSize(self.target)); |
| 1856 | const abi_size = @intCast(u8, ty.abiSize(self.target)); |
| 1857 | 1857 | const opcode = buildOpcode(.{ |
| 1858 | 1858 | .valtype1 = typeToValtype(ty, self.target), |
| 1859 | | .width = abi_size * 8, // use bitsize instead of byte size |
| 1859 | .width = abi_size * 8, |
| 1860 | 1860 | .op = .load, |
| 1861 | 1861 | .signedness = .unsigned, |
| 1862 | 1862 | }); |
| ... | ... | @@ -2106,7 +2106,7 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) |
| 2106 | 2106 | /// Converts a signed integer to its 2's complement form and returns |
| 2107 | 2107 | /// an unsigned integer instead. |
| 2108 | 2108 | /// Asserts bitsize <= 64 |
| 2109 | | fn convertTo2Complement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) { |
| 2109 | fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) { |
| 2110 | 2110 | const T = @TypeOf(value); |
| 2111 | 2111 | comptime assert(@typeInfo(T) == .Int); |
| 2112 | 2112 | comptime assert(@typeInfo(T).Int.signedness == .signed); |
| ... | ... | @@ -2137,7 +2137,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2137 | 2137 | const int_info = ty.intInfo(self.target); |
| 2138 | 2138 | switch (int_info.signedness) { |
| 2139 | 2139 | .signed => switch (int_info.bits) { |
| 2140 | | 0...32 => return WValue{ .imm32 = @intCast(u32, convertTo2Complement( |
| 2140 | 0...32 => return WValue{ .imm32 = @intCast(u32, toTwosComplement( |
| 2141 | 2141 | val.toSignedInt(), |
| 2142 | 2142 | @intCast(u6, int_info.bits), |
| 2143 | 2143 | )) }, |
| ... | ... | @@ -2856,29 +2856,36 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2856 | 2856 | const ty = self.air.getRefType(ty_op.ty); |
| 2857 | 2857 | const operand = try self.resolveInst(ty_op.operand); |
| 2858 | 2858 | const ref_ty = self.air.typeOf(ty_op.operand); |
| 2859 | | const ref_info = ref_ty.intInfo(self.target); |
| 2860 | | const wanted_info = ty.intInfo(self.target); |
| 2859 | if (ty.abiSize(self.target) > 8 or ref_ty.abiSize(self.target) > 8) { |
| 2860 | return self.fail("todo Wasm intcast for bitsize > 64", .{}); |
| 2861 | } |
| 2862 | return self.intcast(operand, ty, ref_ty); |
| 2863 | } |
| 2861 | 2864 | |
| 2862 | | const op_bits = toWasmBits(ref_info.bits) orelse |
| 2863 | | return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{ref_info.bits}); |
| 2864 | | const wanted_bits = toWasmBits(wanted_info.bits) orelse |
| 2865 | | return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{wanted_info.bits}); |
| 2865 | /// Upcasts or downcasts an integer based on the given and wanted types, |
| 2866 | /// and stores the result in a new operand. |
| 2867 | /// Asserts type's bitsize <= 64 |
| 2868 | fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 2869 | const given_info = given.intInfo(self.target); |
| 2870 | const wanted_info = wanted.intInfo(self.target); |
| 2871 | assert(given_info.bits <= 64); |
| 2872 | assert(wanted_info.bits <= 64); |
| 2866 | 2873 | |
| 2867 | | // hot path |
| 2874 | const op_bits = toWasmBits(given_info.bits).?; |
| 2875 | const wanted_bits = toWasmBits(wanted_info.bits).?; |
| 2868 | 2876 | if (op_bits == wanted_bits) return operand; |
| 2869 | 2877 | |
| 2878 | try self.emitWValue(operand); |
| 2870 | 2879 | if (op_bits > 32 and wanted_bits == 32) { |
| 2871 | | try self.emitWValue(operand); |
| 2872 | 2880 | try self.addTag(.i32_wrap_i64); |
| 2873 | 2881 | } else if (op_bits == 32 and wanted_bits > 32) { |
| 2874 | | try self.emitWValue(operand); |
| 2875 | | try self.addTag(switch (ref_info.signedness) { |
| 2882 | try self.addTag(switch (wanted_info.signedness) { |
| 2876 | 2883 | .signed => .i64_extend_i32_s, |
| 2877 | 2884 | .unsigned => .i64_extend_i32_u, |
| 2878 | 2885 | }); |
| 2879 | 2886 | } else unreachable; |
| 2880 | 2887 | |
| 2881 | | const result = try self.allocLocal(ty); |
| 2888 | const result = try self.allocLocal(wanted); |
| 2882 | 2889 | try self.addLabel(.local_set, result.local); |
| 2883 | 2890 | return result; |
| 2884 | 2891 | } |
| ... | ... | @@ -3983,7 +3990,7 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 3983 | 3990 | const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); |
| 3984 | 3991 | try self.emitWValue(cmp_res); |
| 3985 | 3992 | try self.addLabel(.local_set, overflow_bit.local); |
| 3986 | | } else if (int_info.signedness == .signed and op != .shl) { |
| 3993 | } else if (int_info.signedness == .signed and op != .shl and op != .mul) { |
| 3987 | 3994 | // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow. |
| 3988 | 3995 | // We first create an outer block, where we handle overflow. |
| 3989 | 3996 | // Then we create an inner block, where underflow is handled. |
| ... | ... | @@ -4032,9 +4039,36 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 4032 | 4039 | try self.addLabel(.local_set, tmp_val.local); |
| 4033 | 4040 | break :blk tmp_val; |
| 4034 | 4041 | } else if (op == .mul) blk: { |
| 4035 | | if (int_info.signedness == .signed) { |
| 4036 | | const shift_val = convertTo2Complement(-@intCast(i17, int_info.bits), @intCast(u7, int_info.bits)); |
| 4037 | | const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = shift_val } else WValue{ .imm64 = shift_val }; |
| 4042 | // for 32 & 64 bitsize we calculate overflow |
| 4043 | // differently. |
| 4044 | if (int_info.bits == 32) { |
| 4045 | const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; |
| 4046 | const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty); |
| 4047 | const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty); |
| 4048 | const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, op); |
| 4049 | if (int_info.signedness == .unsigned) { |
| 4050 | const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); |
| 4051 | const wrap = try self.intcast(shr, new_ty, lhs_ty); |
| 4052 | const cmp_res = try self.cmp(wrap, zero, lhs_ty, .neq); |
| 4053 | try self.emitWValue(cmp_res); |
| 4054 | try self.addLabel(.local_set, overflow_bit.local); |
| 4055 | break :blk try self.intcast(bin_op, new_ty, lhs_ty); |
| 4056 | } else { |
| 4057 | const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); |
| 4058 | const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); |
| 4059 | |
| 4060 | const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); |
| 4061 | const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); |
| 4062 | const cmp_res = try self.cmp(down_shr_res, shr, lhs_ty, .neq); |
| 4063 | try self.emitWValue(cmp_res); |
| 4064 | try self.addLabel(.local_set, overflow_bit.local); |
| 4065 | break :blk down_cast; |
| 4066 | } |
| 4067 | } else if (int_info.signedness == .signed) { |
| 4068 | const shift_imm = if (wasm_bits == 32) |
| 4069 | WValue{ .imm32 = wasm_bits - int_info.bits } |
| 4070 | else |
| 4071 | WValue{ .imm64 = wasm_bits - int_info.bits }; |
| 4038 | 4072 | |
| 4039 | 4073 | const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl); |
| 4040 | 4074 | const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr); |
| ... | ... | @@ -4051,8 +4085,10 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 4051 | 4085 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4052 | 4086 | } else { |
| 4053 | 4087 | const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); |
| 4054 | | const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = int_info.bits } else WValue{ .imm64 = int_info.bits }; |
| 4055 | | // const zero = if (wasm_bits == 32) WValue{ .imm32 = 0 } else WValue{ .imm64 = 0 }; |
| 4088 | const shift_imm = if (wasm_bits == 32) |
| 4089 | WValue{ .imm32 = int_info.bits } |
| 4090 | else |
| 4091 | WValue{ .imm64 = int_info.bits }; |
| 4056 | 4092 | const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr); |
| 4057 | 4093 | const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq); |
| 4058 | 4094 | try self.emitWValue(cmp_op); |