| ... | @@ -1818,7 +1818,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1818,7 +1818,7 @@ 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.abiSize(self.target)); | 1821 | const abi_size = @intCast(u8, ty.bitSize(self.target)); |
| 1822 | | 1822 | |
| 1823 | const opcode = buildOpcode(.{ | 1823 | const opcode = buildOpcode(.{ |
| 1824 | .valtype1 = valtype, | 1824 | .valtype1 = valtype, |
| ... | @@ -1852,21 +1852,13 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1852,21 +1852,13 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1852 | fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { | 1852 | 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 | // 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 | const opcode = buildOpcode(.{ | 1857 | const opcode = buildOpcode(.{ |
| 1866 | .valtype1 = typeToValtype(ty, self.target), | 1858 | .valtype1 = typeToValtype(ty, self.target), |
| 1867 | .width = abi_size * 8, // use bitsize instead of byte size | 1859 | .width = abi_size * 8, // use bitsize instead of byte size |
| 1868 | .op = .load, | 1860 | .op = .load, |
| 1869 | .signedness = signedness, | 1861 | .signedness = .unsigned, |
| 1870 | }); | 1862 | }); |
| 1871 | | 1863 | |
| 1872 | try self.addMemArg( | 1864 | try self.addMemArg( |
| ... | @@ -1948,6 +1940,7 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError | ... | @@ -1948,6 +1940,7 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError |
| 1948 | .signedness = if (ty.isSignedInt()) .signed else .unsigned, | 1940 | .signedness = if (ty.isSignedInt()) .signed else .unsigned, |
| 1949 | }); | 1941 | }); |
| 1950 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 1942 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| | 1943 | const bin_local = try self.allocLocal(ty); |
| 1951 | | 1944 | |
| 1952 | const int_info = ty.intInfo(self.target); | 1945 | const int_info = ty.intInfo(self.target); |
| 1953 | const bitsize = int_info.bits; | 1946 | const bitsize = int_info.bits; |
| ... | @@ -1963,25 +1956,37 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError | ... | @@ -1963,25 +1956,37 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError |
| 1963 | } else if (is_signed and bitsize == 16) { | 1956 | } else if (is_signed and bitsize == 16) { |
| 1964 | try self.addTag(.i32_extend16_s); | 1957 | try self.addTag(.i32_extend16_s); |
| 1965 | } else { | 1958 | } else { |
| 1966 | const result = (@as(u64, 1) << @intCast(u6, bitsize - @boolToInt(is_signed))) - 1; | 1959 | try self.addLabel(.local_set, bin_local.local); |
| 1967 | if (bitsize < 32) { | 1960 | return self.wrapOperand(bin_local, ty); |
| 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 | } | | |
| 1974 | } | 1961 | } |
| 1975 | } else if (int_info.bits > 64) { | 1962 | } else if (int_info.bits > 64) { |
| 1976 | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); | 1963 | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); |
| 1977 | } | 1964 | } |
| 1978 | | 1965 | |
| 1979 | // save the result in a temporary | 1966 | // save the result in a temporary |
| 1980 | const bin_local = try self.allocLocal(ty); | | |
| 1981 | try self.addLabel(.local_set, bin_local.local); | 1967 | try self.addLabel(.local_set, bin_local.local); |
| 1982 | return bin_local; | 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 | fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue { | 1990 | fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue { |
| 1986 | switch (ptr_val.tag()) { | 1991 | switch (ptr_val.tag()) { |
| 1987 | .decl_ref_mut => { | 1992 | .decl_ref_mut => { |
| ... | @@ -2098,6 +2103,22 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) | ... | @@ -2098,6 +2103,22 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) |
| 2098 | } else return WValue{ .memory = target_sym_index }; | 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 | fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { | 2122 | fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2102 | if (val.isUndefDeep()) return self.emitUndefined(ty); | 2123 | if (val.isUndefDeep()) return self.emitUndefined(ty); |
| 2103 | if (val.castTag(.decl_ref)) |decl_ref| { | 2124 | if (val.castTag(.decl_ref)) |decl_ref| { |
| ... | @@ -2114,10 +2135,12 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { | ... | @@ -2114,10 +2135,12 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2114 | switch (ty.zigTypeTag()) { | 2135 | switch (ty.zigTypeTag()) { |
| 2115 | .Int => { | 2136 | .Int => { |
| 2116 | const int_info = ty.intInfo(self.target); | 2137 | const int_info = ty.intInfo(self.target); |
| 2117 | // write constant | | |
| 2118 | switch (int_info.signedness) { | 2138 | switch (int_info.signedness) { |
| 2119 | .signed => switch (int_info.bits) { | 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 | 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) }, | 2144 | 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) }, |
| 2122 | else => unreachable, | 2145 | else => unreachable, |
| 2123 | }, | 2146 | }, |
| ... | @@ -4009,22 +4032,33 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue | ... | @@ -4009,22 +4032,33 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 4009 | try self.addLabel(.local_set, tmp_val.local); | 4032 | try self.addLabel(.local_set, tmp_val.local); |
| 4010 | break :blk tmp_val; | 4033 | break :blk tmp_val; |
| 4011 | } else if (op == .mul) blk: { | 4034 | } else if (op == .mul) blk: { |
| 4012 | const bin_op = try self.wrapBinOp(lhs, rhs, lhs_ty, op); | 4035 | if (int_info.signedness == .signed) { |
| 4013 | try self.startBlock(.block, wasm.block_empty); | 4036 | const shift_val = convertTo2Complement(-@intCast(i17, int_info.bits), @intCast(u7, int_info.bits)); |
| 4014 | // check if 0. true => Break out of block as cannot over -or underflow. | 4037 | const shift_imm = if (wasm_bits == 32) WValue{ .imm32 = shift_val } else WValue{ .imm64 = shift_val }; |
| 4015 | try self.emitWValue(lhs); | 4038 | |
| 4016 | switch (wasm_bits) { | 4039 | const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl); |
| 4017 | 32 => try self.addTag(.i32_eqz), | 4040 | const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr); |
| 4018 | 64 => try self.addTag(.i64_eqz), | 4041 | const rhs_shl = try self.binOp(rhs, shift_imm, lhs_ty, .shl); |
| 4019 | else => unreachable, | 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 | } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); | 4062 | } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); |
| 4029 | | 4063 | |
| 4030 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); | 4064 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |