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