| ... | ... | @@ -1424,7 +1424,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1424 | 1424 | .add_with_overflow => self.airBinOpOverflow(inst, .add), |
| 1425 | 1425 | .sub_with_overflow => self.airBinOpOverflow(inst, .sub), |
| 1426 | 1426 | .shl_with_overflow => self.airBinOpOverflow(inst, .shl), |
| 1427 | | .mul_with_overflow => self.airBinOpOverflow(inst, .mul), |
| 1427 | .mul_with_overflow => self.airMulWithOverflow(inst), |
| 1428 | 1428 | |
| 1429 | 1429 | .clz => self.airClz(inst), |
| 1430 | 1430 | .ctz => self.airCtz(inst), |
| ... | ... | @@ -1822,7 +1822,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 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 | |
| ... | ... | @@ -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 | 1855 | |
| 1863 | 1856 | const abi_size = @intCast(u8, ty.abiSize(self.target)); |
| 1864 | | |
| 1865 | 1857 | const opcode = buildOpcode(.{ |
| 1866 | 1858 | .valtype1 = typeToValtype(ty, self.target), |
| 1867 | | .width = abi_size * 8, // use bitsize instead of byte size |
| 1859 | .width = abi_size * 8, |
| 1868 | 1860 | .op = .load, |
| 1869 | | .signedness = signedness, |
| 1861 | .signedness = .unsigned, |
| 1870 | 1862 | }); |
| 1871 | 1863 | |
| 1872 | 1864 | try self.addMemArg( |
| ... | ... | @@ -1935,7 +1927,14 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1935 | 1927 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1936 | 1928 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1937 | 1929 | |
| 1938 | | return self.wrapBinOp(lhs, rhs, self.air.typeOf(bin_op.lhs), op); |
| 1930 | const ty = self.air.typeOf(bin_op.lhs); |
| 1931 | if (ty.zigTypeTag() == .Vector) { |
| 1932 | return self.fail("TODO: Implement wrapping arithmetic for vectors", .{}); |
| 1933 | } else if (ty.abiSize(self.target) > 8) { |
| 1934 | return self.fail("TODO: Implement wrapping arithmetic for bitsize > 64", .{}); |
| 1935 | } |
| 1936 | |
| 1937 | return self.wrapBinOp(lhs, rhs, ty, op); |
| 1939 | 1938 | } |
| 1940 | 1939 | |
| 1941 | 1940 | fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| ... | ... | @@ -1948,38 +1947,28 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError |
| 1948 | 1947 | .signedness = if (ty.isSignedInt()) .signed else .unsigned, |
| 1949 | 1948 | }); |
| 1950 | 1949 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1951 | | |
| 1952 | | const int_info = ty.intInfo(self.target); |
| 1953 | | const bitsize = int_info.bits; |
| 1954 | | const is_signed = int_info.signedness == .signed; |
| 1955 | | // if target type bitsize is x < 32 and 32 > x < 64, we perform |
| 1956 | | // result & ((1<<N)-1) where N = bitsize or bitsize -1 incase of signed. |
| 1957 | | if (bitsize != 32 and bitsize < 64) { |
| 1958 | | // first check if we can use a single instruction, |
| 1959 | | // wasm provides those if the integers are signed and 8/16-bit. |
| 1960 | | // For arbitrary integer sizes, we use the algorithm mentioned above. |
| 1961 | | if (is_signed and bitsize == 8) { |
| 1962 | | try self.addTag(.i32_extend8_s); |
| 1963 | | } else if (is_signed and bitsize == 16) { |
| 1964 | | try self.addTag(.i32_extend16_s); |
| 1965 | | } 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 | | } |
| 1974 | | } |
| 1975 | | } else if (int_info.bits > 64) { |
| 1976 | | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); |
| 1977 | | } |
| 1978 | | |
| 1979 | | // save the result in a temporary |
| 1980 | 1950 | const bin_local = try self.allocLocal(ty); |
| 1981 | 1951 | try self.addLabel(.local_set, bin_local.local); |
| 1982 | | return bin_local; |
| 1952 | return self.wrapOperand(bin_local, ty); |
| 1953 | } |
| 1954 | |
| 1955 | /// Wraps an operand based on a given type's bitsize. |
| 1956 | /// Asserts `Type` is <= 64bits. |
| 1957 | fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { |
| 1958 | assert(ty.abiSize(self.target) <= 8); |
| 1959 | const result_local = try self.allocLocal(ty); |
| 1960 | const bitsize = ty.intInfo(self.target).bits; |
| 1961 | const result = @intCast(u64, (@as(u65, 1) << @intCast(u7, bitsize)) - 1); |
| 1962 | try self.emitWValue(operand); |
| 1963 | if (bitsize <= 32) { |
| 1964 | try self.addImm32(@bitCast(i32, @intCast(u32, result))); |
| 1965 | try self.addTag(.i32_and); |
| 1966 | } else { |
| 1967 | try self.addImm64(result); |
| 1968 | try self.addTag(.i64_and); |
| 1969 | } |
| 1970 | try self.addLabel(.local_set, result_local.local); |
| 1971 | return result_local; |
| 1983 | 1972 | } |
| 1984 | 1973 | |
| 1985 | 1974 | fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue { |
| ... | ... | @@ -2098,6 +2087,22 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) |
| 2098 | 2087 | } else return WValue{ .memory = target_sym_index }; |
| 2099 | 2088 | } |
| 2100 | 2089 | |
| 2090 | /// Converts a signed integer to its 2's complement form and returns |
| 2091 | /// an unsigned integer instead. |
| 2092 | /// Asserts bitsize <= 64 |
| 2093 | fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) { |
| 2094 | const T = @TypeOf(value); |
| 2095 | comptime assert(@typeInfo(T) == .Int); |
| 2096 | comptime assert(@typeInfo(T).Int.signedness == .signed); |
| 2097 | assert(bits <= 64); |
| 2098 | const WantedT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); |
| 2099 | if (value >= 0) return @bitCast(WantedT, value); |
| 2100 | const max_value = @intCast(u64, (@as(u65, 1) << bits) - 1); |
| 2101 | const flipped = (~-value) + 1; |
| 2102 | const result = @bitCast(WantedT, flipped) & max_value; |
| 2103 | return @intCast(WantedT, result); |
| 2104 | } |
| 2105 | |
| 2101 | 2106 | fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2102 | 2107 | if (val.isUndefDeep()) return self.emitUndefined(ty); |
| 2103 | 2108 | if (val.castTag(.decl_ref)) |decl_ref| { |
| ... | ... | @@ -2114,10 +2119,12 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 2114 | 2119 | switch (ty.zigTypeTag()) { |
| 2115 | 2120 | .Int => { |
| 2116 | 2121 | const int_info = ty.intInfo(self.target); |
| 2117 | | // write constant |
| 2118 | 2122 | switch (int_info.signedness) { |
| 2119 | 2123 | .signed => switch (int_info.bits) { |
| 2120 | | 0...32 => return WValue{ .imm32 = @bitCast(u32, @intCast(i32, val.toSignedInt())) }, |
| 2124 | 0...32 => return WValue{ .imm32 = @intCast(u32, toTwosComplement( |
| 2125 | val.toSignedInt(), |
| 2126 | @intCast(u6, int_info.bits), |
| 2127 | )) }, |
| 2121 | 2128 | 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) }, |
| 2122 | 2129 | else => unreachable, |
| 2123 | 2130 | }, |
| ... | ... | @@ -2832,30 +2839,38 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2832 | 2839 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2833 | 2840 | const ty = self.air.getRefType(ty_op.ty); |
| 2834 | 2841 | const operand = try self.resolveInst(ty_op.operand); |
| 2835 | | const ref_ty = self.air.typeOf(ty_op.operand); |
| 2836 | | const ref_info = ref_ty.intInfo(self.target); |
| 2837 | | const wanted_info = ty.intInfo(self.target); |
| 2842 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 2843 | if (ty.abiSize(self.target) > 8 or operand_ty.abiSize(self.target) > 8) { |
| 2844 | return self.fail("todo Wasm intcast for bitsize > 64", .{}); |
| 2845 | } |
| 2838 | 2846 | |
| 2839 | | const op_bits = toWasmBits(ref_info.bits) orelse |
| 2840 | | return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{ref_info.bits}); |
| 2841 | | const wanted_bits = toWasmBits(wanted_info.bits) orelse |
| 2842 | | return self.fail("TODO: Wasm intcast integer types of bitsize: {d}", .{wanted_info.bits}); |
| 2847 | return self.intcast(operand, operand_ty, ty); |
| 2848 | } |
| 2849 | |
| 2850 | /// Upcasts or downcasts an integer based on the given and wanted types, |
| 2851 | /// and stores the result in a new operand. |
| 2852 | /// Asserts type's bitsize <= 64 |
| 2853 | fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 2854 | const given_info = given.intInfo(self.target); |
| 2855 | const wanted_info = wanted.intInfo(self.target); |
| 2856 | assert(given_info.bits <= 64); |
| 2857 | assert(wanted_info.bits <= 64); |
| 2843 | 2858 | |
| 2844 | | // hot path |
| 2859 | const op_bits = toWasmBits(given_info.bits).?; |
| 2860 | const wanted_bits = toWasmBits(wanted_info.bits).?; |
| 2845 | 2861 | if (op_bits == wanted_bits) return operand; |
| 2846 | 2862 | |
| 2863 | try self.emitWValue(operand); |
| 2847 | 2864 | if (op_bits > 32 and wanted_bits == 32) { |
| 2848 | | try self.emitWValue(operand); |
| 2849 | 2865 | try self.addTag(.i32_wrap_i64); |
| 2850 | 2866 | } else if (op_bits == 32 and wanted_bits > 32) { |
| 2851 | | try self.emitWValue(operand); |
| 2852 | | try self.addTag(switch (ref_info.signedness) { |
| 2867 | try self.addTag(switch (wanted_info.signedness) { |
| 2853 | 2868 | .signed => .i64_extend_i32_s, |
| 2854 | 2869 | .unsigned => .i64_extend_i32_u, |
| 2855 | 2870 | }); |
| 2856 | 2871 | } else unreachable; |
| 2857 | 2872 | |
| 2858 | | const result = try self.allocLocal(ty); |
| 2873 | const result = try self.allocLocal(wanted); |
| 2859 | 2874 | try self.addLabel(.local_set, result.local); |
| 2860 | 2875 | return result; |
| 2861 | 2876 | } |
| ... | ... | @@ -3072,63 +3087,17 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3072 | 3087 | } |
| 3073 | 3088 | |
| 3074 | 3089 | fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3075 | | if (self.liveness.isUnused(inst)) return WValue.none; |
| 3090 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 3076 | 3091 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3077 | 3092 | const operand = try self.resolveInst(ty_op.operand); |
| 3078 | | const op_ty = self.air.typeOf(ty_op.operand); |
| 3079 | | const int_info = self.air.getRefType(ty_op.ty).intInfo(self.target); |
| 3093 | const wanted_ty = self.air.getRefType(ty_op.ty); |
| 3094 | const int_info = wanted_ty.intInfo(self.target); |
| 3080 | 3095 | const wanted_bits = int_info.bits; |
| 3081 | | const result = try self.allocLocal(self.air.getRefType(ty_op.ty)); |
| 3082 | | const op_bits = op_ty.intInfo(self.target).bits; |
| 3083 | 3096 | |
| 3084 | | const wasm_bits = toWasmBits(wanted_bits) orelse |
| 3097 | _ = toWasmBits(wanted_bits) orelse { |
| 3085 | 3098 | return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits}); |
| 3086 | | |
| 3087 | | // Use wasm's instruction to wrap from 64bit to 32bit integer when possible |
| 3088 | | if (op_bits == 64 and wanted_bits == 32) { |
| 3089 | | try self.emitWValue(operand); |
| 3090 | | try self.addTag(.i32_wrap_i64); |
| 3091 | | try self.addLabel(.local_set, result.local); |
| 3092 | | return result; |
| 3093 | | } |
| 3094 | | |
| 3095 | | // Any other truncation must be done manually |
| 3096 | | if (int_info.signedness == .unsigned) { |
| 3097 | | const mask = (@as(u65, 1) << @intCast(u7, wanted_bits)) - 1; |
| 3098 | | try self.emitWValue(operand); |
| 3099 | | switch (wasm_bits) { |
| 3100 | | 32 => { |
| 3101 | | try self.addImm32(@bitCast(i32, @intCast(u32, mask))); |
| 3102 | | try self.addTag(.i32_and); |
| 3103 | | }, |
| 3104 | | 64 => { |
| 3105 | | try self.addImm64(@intCast(u64, mask)); |
| 3106 | | try self.addTag(.i64_and); |
| 3107 | | }, |
| 3108 | | else => unreachable, |
| 3109 | | } |
| 3110 | | } else { |
| 3111 | | const shift_bits = wasm_bits - wanted_bits; |
| 3112 | | try self.emitWValue(operand); |
| 3113 | | switch (wasm_bits) { |
| 3114 | | 32 => { |
| 3115 | | try self.addImm32(@bitCast(i16, shift_bits)); |
| 3116 | | try self.addTag(.i32_shl); |
| 3117 | | try self.addImm32(@bitCast(i16, shift_bits)); |
| 3118 | | try self.addTag(.i32_shr_s); |
| 3119 | | }, |
| 3120 | | 64 => { |
| 3121 | | try self.addImm64(shift_bits); |
| 3122 | | try self.addTag(.i64_shl); |
| 3123 | | try self.addImm64(shift_bits); |
| 3124 | | try self.addTag(.i64_shr_s); |
| 3125 | | }, |
| 3126 | | else => unreachable, |
| 3127 | | } |
| 3128 | | } |
| 3129 | | |
| 3130 | | try self.addLabel(.local_set, result.local); |
| 3131 | | return result; |
| 3099 | }; |
| 3100 | return self.wrapOperand(operand, wanted_ty); |
| 3132 | 3101 | } |
| 3133 | 3102 | |
| 3134 | 3103 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3418,7 +3387,8 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3418 | 3387 | |
| 3419 | 3388 | const result = try self.allocLocal(dest_ty); |
| 3420 | 3389 | try self.addLabel(.local_set, result.local); |
| 3421 | | return result; |
| 3390 | |
| 3391 | return self.wrapOperand(result, dest_ty); |
| 3422 | 3392 | } |
| 3423 | 3393 | |
| 3424 | 3394 | fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3922,6 +3892,10 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 3922 | 3892 | const rhs = try self.resolveInst(extra.rhs); |
| 3923 | 3893 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 3924 | 3894 | |
| 3895 | if (lhs_ty.zigTypeTag() == .Vector) { |
| 3896 | return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 3897 | } |
| 3898 | |
| 3925 | 3899 | // We store the bit if it's overflowed or not in this. As it's zero-initialized |
| 3926 | 3900 | // we only need to update it if an overflow (or underflow) occured. |
| 3927 | 3901 | const overflow_bit = try self.allocLocal(Type.initTag(.u1)); |
| ... | ... | @@ -4008,24 +3982,100 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 4008 | 3982 | } |
| 4009 | 3983 | try self.addLabel(.local_set, tmp_val.local); |
| 4010 | 3984 | break :blk tmp_val; |
| 4011 | | } 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, |
| 3985 | } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); |
| 3986 | |
| 3987 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 3988 | try self.store(result_ptr, bin_op, lhs_ty, 0); |
| 3989 | const offset = @intCast(u32, lhs_ty.abiSize(self.target)); |
| 3990 | try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); |
| 3991 | |
| 3992 | return result_ptr; |
| 3993 | } |
| 3994 | |
| 3995 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3996 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3997 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3998 | const lhs = try self.resolveInst(extra.lhs); |
| 3999 | const rhs = try self.resolveInst(extra.rhs); |
| 4000 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 4001 | |
| 4002 | if (lhs_ty.zigTypeTag() == .Vector) { |
| 4003 | return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 4004 | } |
| 4005 | |
| 4006 | // We store the bit if it's overflowed or not in this. As it's zero-initialized |
| 4007 | // we only need to update it if an overflow (or underflow) occured. |
| 4008 | const overflow_bit = try self.allocLocal(Type.initTag(.u1)); |
| 4009 | const int_info = lhs_ty.intInfo(self.target); |
| 4010 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 4011 | return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits}); |
| 4012 | }; |
| 4013 | |
| 4014 | if (wasm_bits == 64) { |
| 4015 | return self.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits}); |
| 4016 | } |
| 4017 | |
| 4018 | const zero = switch (wasm_bits) { |
| 4019 | 32 => WValue{ .imm32 = 0 }, |
| 4020 | 64 => WValue{ .imm64 = 0 }, |
| 4021 | else => unreachable, |
| 4022 | }; |
| 4023 | |
| 4024 | // for 32 bit integers we upcast it to a 64bit integer |
| 4025 | const bin_op = if (int_info.bits == 32) blk: { |
| 4026 | const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; |
| 4027 | const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty); |
| 4028 | const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty); |
| 4029 | const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, .mul); |
| 4030 | if (int_info.signedness == .unsigned) { |
| 4031 | const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); |
| 4032 | const wrap = try self.intcast(shr, new_ty, lhs_ty); |
| 4033 | const cmp_res = try self.cmp(wrap, zero, lhs_ty, .neq); |
| 4034 | try self.emitWValue(cmp_res); |
| 4035 | try self.addLabel(.local_set, overflow_bit.local); |
| 4036 | break :blk try self.intcast(bin_op, new_ty, lhs_ty); |
| 4037 | } else { |
| 4038 | const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); |
| 4039 | const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); |
| 4040 | |
| 4041 | const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); |
| 4042 | const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); |
| 4043 | const cmp_res = try self.cmp(down_shr_res, shr, lhs_ty, .neq); |
| 4044 | try self.emitWValue(cmp_res); |
| 4045 | try self.addLabel(.local_set, overflow_bit.local); |
| 4046 | break :blk down_cast; |
| 4020 | 4047 | } |
| 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); |
| 4048 | } else if (int_info.signedness == .signed) blk: { |
| 4049 | const shift_imm = if (wasm_bits == 32) |
| 4050 | WValue{ .imm32 = wasm_bits - int_info.bits } |
| 4051 | else |
| 4052 | WValue{ .imm64 = wasm_bits - int_info.bits }; |
| 4053 | |
| 4054 | const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl); |
| 4055 | const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr); |
| 4056 | const rhs_shl = try self.binOp(rhs, shift_imm, lhs_ty, .shl); |
| 4057 | const rhs_shr = try self.binOp(rhs_shl, shift_imm, lhs_ty, .shr); |
| 4058 | |
| 4059 | const bin_op = try self.binOp(lhs_shr, rhs_shr, lhs_ty, .mul); |
| 4060 | const shl = try self.binOp(bin_op, shift_imm, lhs_ty, .shl); |
| 4061 | const shr = try self.binOp(shl, shift_imm, lhs_ty, .shr); |
| 4062 | |
| 4063 | const cmp_op = try self.cmp(shr, bin_op, lhs_ty, .neq); |
| 4064 | try self.emitWValue(cmp_op); |
| 4025 | 4065 | 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); |
| 4066 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4067 | } else blk: { |
| 4068 | const bin_op = try self.binOp(lhs, rhs, lhs_ty, .mul); |
| 4069 | const shift_imm = if (wasm_bits == 32) |
| 4070 | WValue{ .imm32 = int_info.bits } |
| 4071 | else |
| 4072 | WValue{ .imm64 = int_info.bits }; |
| 4073 | const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr); |
| 4074 | const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq); |
| 4075 | try self.emitWValue(cmp_op); |
| 4076 | try self.addLabel(.local_set, overflow_bit.local); |
| 4077 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4078 | }; |
| 4029 | 4079 | |
| 4030 | 4080 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4031 | 4081 | try self.store(result_ptr, bin_op, lhs_ty, 0); |