| ... | ... | @@ -1818,7 +1818,7 @@ 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.abiSize(self.target)); |
| 1821 | const abi_size = @intCast(u8, ty.bitSize(self.target)); |
| 1822 | 1822 | |
| 1823 | 1823 | const opcode = buildOpcode(.{ |
| 1824 | 1824 | .valtype1 = valtype, |
| ... | ... | @@ -1852,21 +1852,13 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1852 | 1852 | 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 | | // Build the opcode with the right bitsize |
| 1856 | | const signedness: std.builtin.Signedness = if (ty.isUnsignedInt() or |
| 1857 | | ty.zigTypeTag() == .ErrorSet or |
| 1858 | | ty.zigTypeTag() == .Bool) |
| 1859 | | .unsigned |
| 1860 | | else |
| 1861 | | .signed; |
| 1862 | | |
| 1863 | | const abi_size = @intCast(u8, ty.abiSize(self.target)); |
| 1864 | 1855 | |
| 1856 | const abi_size = @intCast(u8, ty.bitSize(self.target)); |
| 1865 | 1857 | const opcode = buildOpcode(.{ |
| 1866 | 1858 | .valtype1 = typeToValtype(ty, self.target), |
| 1867 | 1859 | .width = abi_size * 8, // use bitsize instead of byte size |
| 1868 | 1860 | .op = .load, |
| 1869 | | .signedness = signedness, |
| 1861 | .signedness = .unsigned, |
| 1870 | 1862 | }); |
| 1871 | 1863 | |
| 1872 | 1864 | try self.addMemArg( |
| ... | ... | @@ -1948,6 +1940,7 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError |
| 1948 | 1940 | .signedness = if (ty.isSignedInt()) .signed else .unsigned, |
| 1949 | 1941 | }); |
| 1950 | 1942 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1943 | const bin_local = try self.allocLocal(ty); |
| 1951 | 1944 | |
| 1952 | 1945 | const int_info = ty.intInfo(self.target); |
| 1953 | 1946 | const bitsize = int_info.bits; |
| ... | ... | @@ -1963,25 +1956,37 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError |
| 1963 | 1956 | } else if (is_signed and bitsize == 16) { |
| 1964 | 1957 | try self.addTag(.i32_extend16_s); |
| 1965 | 1958 | } else { |
| 1966 | | const result = (@as(u64, 1) << @intCast(u6, bitsize - @boolToInt(is_signed))) - 1; |
| 1967 | | if (bitsize < 32) { |
| 1968 | | try self.addImm32(@bitCast(i32, @intCast(u32, result))); |
| 1969 | | try self.addTag(.i32_and); |
| 1970 | | } else { |
| 1971 | | try self.addImm64(result); |
| 1972 | | try self.addTag(.i64_and); |
| 1973 | | } |
| 1959 | try self.addLabel(.local_set, bin_local.local); |
| 1960 | return self.wrapOperand(bin_local, ty); |
| 1974 | 1961 | } |
| 1975 | 1962 | } else if (int_info.bits > 64) { |
| 1976 | 1963 | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); |
| 1977 | 1964 | } |
| 1978 | 1965 | |
| 1979 | 1966 | // save the result in a temporary |
| 1980 | | const bin_local = try self.allocLocal(ty); |
| 1981 | 1967 | try self.addLabel(.local_set, bin_local.local); |
| 1982 | 1968 | return bin_local; |
| 1983 | 1969 | } |
| 1984 | 1970 | |
| 1971 | /// Wraps an operand based on a given type's bitsize. |
| 1972 | /// Asserts `Type` is <= 64bits. |
| 1973 | fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { |
| 1974 | assert(ty.abiSize(self.target) <= 8); |
| 1975 | const result_local = try self.allocLocal(ty); |
| 1976 | const bitsize = ty.intInfo(self.target).bits; |
| 1977 | const result = @intCast(u64, (@as(u65, 1) << @intCast(u7, bitsize)) - 1); |
| 1978 | try self.emitWValue(operand); |
| 1979 | if (bitsize <= 32) { |
| 1980 | try self.addImm32(@bitCast(i32, @intCast(u32, result))); |
| 1981 | try self.addTag(.i32_and); |
| 1982 | } else { |
| 1983 | try self.addImm64(result); |
| 1984 | try self.addTag(.i64_and); |
| 1985 | } |
| 1986 | try self.addLabel(.local_set, result_local.local); |
| 1987 | return result_local; |
| 1988 | } |
| 1989 | |
| 1985 | 1990 | fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue { |
| 1986 | 1991 | switch (ptr_val.tag()) { |
| 1987 | 1992 | .decl_ref_mut => { |
| ... | ... | @@ -2098,6 +2103,22 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) |
| 2098 | 2103 | } else return WValue{ .memory = target_sym_index }; |
| 2099 | 2104 | } |
| 2100 | 2105 | |
| 2106 | /// Converts a signed integer to its 2's complement form and returns |
| 2107 | /// an unsigned integer instead. |
| 2108 | /// Asserts bitsize <= 64 |
| 2109 | fn convertTo2Complement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) { |
| 2110 | const T = @TypeOf(value); |
| 2111 | comptime assert(@typeInfo(T) == .Int); |
| 2112 | comptime assert(@typeInfo(T).Int.signedness == .signed); |
| 2113 | assert(bits <= 64); |
| 2114 | const WantedT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); |
| 2115 | if (value >= 0) return @bitCast(WantedT, value); |
| 2116 | const max_value = @intCast(u64, (@as(u65, 1) << bits) - 1); |
| 2117 | const flipped = (~-value) + 1; |
| 2118 | const result = @bitCast(WantedT, flipped) & max_value; |
| 2119 | return @intCast(WantedT, result); |
| 2120 | } |
| 2121 | |
| 2101 | 2122 | fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2102 | 2123 | if (val.isUndefDeep()) return self.emitUndefined(ty); |
| 2103 | 2124 | if (val.castTag(.decl_ref)) |decl_ref| { |
| ... | ... | @@ -2114,10 +2135,12 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2114 | 2135 | switch (ty.zigTypeTag()) { |
| 2115 | 2136 | .Int => { |
| 2116 | 2137 | const int_info = ty.intInfo(self.target); |
| 2117 | | // write constant |
| 2118 | 2138 | switch (int_info.signedness) { |
| 2119 | 2139 | .signed => switch (int_info.bits) { |
| 2120 | | 0...32 => return WValue{ .imm32 = @bitCast(u32, @intCast(i32, val.toSignedInt())) }, |
| 2140 | 0...32 => return WValue{ .imm32 = @intCast(u32, convertTo2Complement( |
| 2141 | val.toSignedInt(), |
| 2142 | @intCast(u6, int_info.bits), |
| 2143 | )) }, |
| 2121 | 2144 | 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) }, |
| 2122 | 2145 | else => unreachable, |
| 2123 | 2146 | }, |
| ... | ... | @@ -4009,22 +4032,33 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 4009 | 4032 | try self.addLabel(.local_set, tmp_val.local); |
| 4010 | 4033 | break :blk tmp_val; |
| 4011 | 4034 | } else if (op == .mul) blk: { |
| 4012 | | const bin_op = try self.wrapBinOp(lhs, rhs, lhs_ty, op); |
| 4013 | | try self.startBlock(.block, wasm.block_empty); |
| 4014 | | // check if 0. true => Break out of block as cannot over -or underflow. |
| 4015 | | try self.emitWValue(lhs); |
| 4016 | | switch (wasm_bits) { |
| 4017 | | 32 => try self.addTag(.i32_eqz), |
| 4018 | | 64 => try self.addTag(.i64_eqz), |
| 4019 | | else => unreachable, |
| 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 }; |
| 4038 | |
| 4039 | 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); |
| 4041 | const rhs_shl = try self.binOp(rhs, shift_imm, lhs_ty, .shl); |
| 4042 | const rhs_shr = try self.binOp(rhs_shl, shift_imm, lhs_ty, .shr); |
| 4043 | |
| 4044 | const bin_op = try self.binOp(lhs_shr, rhs_shr, lhs_ty, op); |
| 4045 | const shl = try self.binOp(bin_op, shift_imm, lhs_ty, .shl); |
| 4046 | const shr = try self.binOp(shl, shift_imm, lhs_ty, .shr); |
| 4047 | |
| 4048 | const cmp_op = try self.cmp(shr, bin_op, lhs_ty, .neq); |
| 4049 | try self.emitWValue(cmp_op); |
| 4050 | try self.addLabel(.local_set, overflow_bit.local); |
| 4051 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4052 | } else { |
| 4053 | 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 }; |
| 4056 | const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr); |
| 4057 | const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq); |
| 4058 | try self.emitWValue(cmp_op); |
| 4059 | try self.addLabel(.local_set, overflow_bit.local); |
| 4060 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4020 | 4061 | } |
| 4021 | | try self.addLabel(.br_if, 0); |
| 4022 | | const div = try self.binOp(bin_op, lhs, lhs_ty, .div); |
| 4023 | | const cmp_res = try self.cmp(div, rhs, lhs_ty, .neq); |
| 4024 | | try self.emitWValue(cmp_res); |
| 4025 | | try self.addLabel(.local_set, overflow_bit.local); |
| 4026 | | try self.endBlock(); |
| 4027 | | break :blk bin_op; |
| 4028 | 4062 | } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); |
| 4029 | 4063 | |
| 4030 | 4064 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |