| ... | @@ -1424,7 +1424,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1424,7 +1424,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1424 | .add_with_overflow => self.airBinOpOverflow(inst, .add), | 1424 | .add_with_overflow => self.airBinOpOverflow(inst, .add), |
| 1425 | .sub_with_overflow => self.airBinOpOverflow(inst, .sub), | 1425 | .sub_with_overflow => self.airBinOpOverflow(inst, .sub), |
| 1426 | .shl_with_overflow => self.airBinOpOverflow(inst, .shl), | 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 | .clz => self.airClz(inst), | 1429 | .clz => self.airClz(inst), |
| 1430 | .ctz => self.airCtz(inst), | 1430 | .ctz => self.airCtz(inst), |
| ... | @@ -1927,7 +1927,14 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1927,7 +1927,14 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1927 | const lhs = try self.resolveInst(bin_op.lhs); | 1927 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1928 | const rhs = try self.resolveInst(bin_op.rhs); | 1928 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1929 | | 1929 | |
| 1930 | 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); |
| 1931 | } | 1938 | } |
| 1932 | | 1939 | |
| 1933 | fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | 1940 | fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| ... | @@ -1941,31 +1948,8 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError | ... | @@ -1941,31 +1948,8 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError |
| 1941 | }); | 1948 | }); |
| 1942 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 1949 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1943 | const bin_local = try self.allocLocal(ty); | 1950 | const bin_local = try self.allocLocal(ty); |
| 1944 | | | |
| 1945 | const int_info = ty.intInfo(self.target); | | |
| 1946 | const bitsize = int_info.bits; | | |
| 1947 | const is_signed = int_info.signedness == .signed; | | |
| 1948 | // if target type bitsize is x < 32 and 32 > x < 64, we perform | | |
| 1949 | // result & ((1<<N)-1) where N = bitsize or bitsize -1 incase of signed. | | |
| 1950 | if (bitsize != 32 and bitsize < 64) { | | |
| 1951 | // first check if we can use a single instruction, | | |
| 1952 | // wasm provides those if the integers are signed and 8/16-bit. | | |
| 1953 | // For arbitrary integer sizes, we use the algorithm mentioned above. | | |
| 1954 | if (is_signed and bitsize == 8) { | | |
| 1955 | try self.addTag(.i32_extend8_s); | | |
| 1956 | } else if (is_signed and bitsize == 16) { | | |
| 1957 | try self.addTag(.i32_extend16_s); | | |
| 1958 | } else { | | |
| 1959 | try self.addLabel(.local_set, bin_local.local); | | |
| 1960 | return self.wrapOperand(bin_local, ty); | | |
| 1961 | } | | |
| 1962 | } else if (int_info.bits > 64) { | | |
| 1963 | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); | | |
| 1964 | } | | |
| 1965 | | | |
| 1966 | // save the result in a temporary | | |
| 1967 | try self.addLabel(.local_set, bin_local.local); | 1951 | try self.addLabel(.local_set, bin_local.local); |
| 1968 | return bin_local; | 1952 | return self.wrapOperand(bin_local, ty); |
| 1969 | } | 1953 | } |
| 1970 | | 1954 | |
| 1971 | /// Wraps an operand based on a given type's bitsize. | 1955 | /// Wraps an operand based on a given type's bitsize. |
| ... | @@ -2855,11 +2839,12 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2855,11 +2839,12 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2855 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2839 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2856 | const ty = self.air.getRefType(ty_op.ty); | 2840 | const ty = self.air.getRefType(ty_op.ty); |
| 2857 | const operand = try self.resolveInst(ty_op.operand); | 2841 | const operand = try self.resolveInst(ty_op.operand); |
| 2858 | const ref_ty = self.air.typeOf(ty_op.operand); | 2842 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 2859 | if (ty.abiSize(self.target) > 8 or ref_ty.abiSize(self.target) > 8) { | 2843 | if (ty.abiSize(self.target) > 8 or operand_ty.abiSize(self.target) > 8) { |
| 2860 | return self.fail("todo Wasm intcast for bitsize > 64", .{}); | 2844 | return self.fail("todo Wasm intcast for bitsize > 64", .{}); |
| 2861 | } | 2845 | } |
| 2862 | return self.intcast(operand, ty, ref_ty); | 2846 | |
| | 2847 | return self.intcast(operand, operand_ty, ty); |
| 2863 | } | 2848 | } |
| 2864 | | 2849 | |
| 2865 | /// Upcasts or downcasts an integer based on the given and wanted types, | 2850 | /// Upcasts or downcasts an integer based on the given and wanted types, |
| ... | @@ -3102,63 +3087,17 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3102,63 +3087,17 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3102 | } | 3087 | } |
| 3103 | | 3088 | |
| 3104 | fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3089 | fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3105 | if (self.liveness.isUnused(inst)) return WValue.none; | 3090 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 3106 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 3091 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3107 | const operand = try self.resolveInst(ty_op.operand); | 3092 | const operand = try self.resolveInst(ty_op.operand); |
| 3108 | const op_ty = self.air.typeOf(ty_op.operand); | 3093 | const wanted_ty = self.air.getRefType(ty_op.ty); |
| 3109 | const int_info = self.air.getRefType(ty_op.ty).intInfo(self.target); | 3094 | const int_info = wanted_ty.intInfo(self.target); |
| 3110 | const wanted_bits = int_info.bits; | 3095 | const wanted_bits = int_info.bits; |
| 3111 | const result = try self.allocLocal(self.air.getRefType(ty_op.ty)); | | |
| 3112 | const op_bits = op_ty.intInfo(self.target).bits; | | |
| 3113 | | 3096 | |
| 3114 | const wasm_bits = toWasmBits(wanted_bits) orelse | 3097 | _ = toWasmBits(wanted_bits) orelse { |
| 3115 | return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits}); | 3098 | return self.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{wanted_bits}); |
| 3116 | | 3099 | }; |
| 3117 | // Use wasm's instruction to wrap from 64bit to 32bit integer when possible | 3100 | return self.wrapOperand(operand, wanted_ty); |
| 3118 | if (op_bits == 64 and wanted_bits == 32) { | | |
| 3119 | try self.emitWValue(operand); | | |
| 3120 | try self.addTag(.i32_wrap_i64); | | |
| 3121 | try self.addLabel(.local_set, result.local); | | |
| 3122 | return result; | | |
| 3123 | } | | |
| 3124 | | | |
| 3125 | // Any other truncation must be done manually | | |
| 3126 | if (int_info.signedness == .unsigned) { | | |
| 3127 | const mask = (@as(u65, 1) << @intCast(u7, wanted_bits)) - 1; | | |
| 3128 | try self.emitWValue(operand); | | |
| 3129 | switch (wasm_bits) { | | |
| 3130 | 32 => { | | |
| 3131 | try self.addImm32(@bitCast(i32, @intCast(u32, mask))); | | |
| 3132 | try self.addTag(.i32_and); | | |
| 3133 | }, | | |
| 3134 | 64 => { | | |
| 3135 | try self.addImm64(@intCast(u64, mask)); | | |
| 3136 | try self.addTag(.i64_and); | | |
| 3137 | }, | | |
| 3138 | else => unreachable, | | |
| 3139 | } | | |
| 3140 | } else { | | |
| 3141 | const shift_bits = wasm_bits - wanted_bits; | | |
| 3142 | try self.emitWValue(operand); | | |
| 3143 | switch (wasm_bits) { | | |
| 3144 | 32 => { | | |
| 3145 | try self.addImm32(@bitCast(i16, shift_bits)); | | |
| 3146 | try self.addTag(.i32_shl); | | |
| 3147 | try self.addImm32(@bitCast(i16, shift_bits)); | | |
| 3148 | try self.addTag(.i32_shr_s); | | |
| 3149 | }, | | |
| 3150 | 64 => { | | |
| 3151 | try self.addImm64(shift_bits); | | |
| 3152 | try self.addTag(.i64_shl); | | |
| 3153 | try self.addImm64(shift_bits); | | |
| 3154 | try self.addTag(.i64_shr_s); | | |
| 3155 | }, | | |
| 3156 | else => unreachable, | | |
| 3157 | } | | |
| 3158 | } | | |
| 3159 | | | |
| 3160 | try self.addLabel(.local_set, result.local); | | |
| 3161 | return result; | | |
| 3162 | } | 3101 | } |
| 3163 | | 3102 | |
| 3164 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3103 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -3448,7 +3387,8 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3448,7 +3387,8 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3448 | | 3387 | |
| 3449 | const result = try self.allocLocal(dest_ty); | 3388 | const result = try self.allocLocal(dest_ty); |
| 3450 | try self.addLabel(.local_set, result.local); | 3389 | try self.addLabel(.local_set, result.local); |
| 3451 | return result; | 3390 | |
| | 3391 | return self.wrapOperand(result, dest_ty); |
| 3452 | } | 3392 | } |
| 3453 | | 3393 | |
| 3454 | fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3394 | fn airIntToFloat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -3952,6 +3892,10 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue | ... | @@ -3952,6 +3892,10 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 3952 | const rhs = try self.resolveInst(extra.rhs); | 3892 | const rhs = try self.resolveInst(extra.rhs); |
| 3953 | const lhs_ty = self.air.typeOf(extra.lhs); | 3893 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 3954 | | 3894 | |
| | 3895 | if (lhs_ty.zigTypeTag() == .Vector) { |
| | 3896 | return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| | 3897 | } |
| | 3898 | |
| 3955 | // We store the bit if it's overflowed or not in this. As it's zero-initialized | 3899 | // We store the bit if it's overflowed or not in this. As it's zero-initialized |
| 3956 | // we only need to update it if an overflow (or underflow) occured. | 3900 | // we only need to update it if an overflow (or underflow) occured. |
| 3957 | const overflow_bit = try self.allocLocal(Type.initTag(.u1)); | 3901 | const overflow_bit = try self.allocLocal(Type.initTag(.u1)); |
| ... | @@ -3990,7 +3934,7 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue | ... | @@ -3990,7 +3934,7 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 3990 | const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); | 3934 | const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); |
| 3991 | try self.emitWValue(cmp_res); | 3935 | try self.emitWValue(cmp_res); |
| 3992 | try self.addLabel(.local_set, overflow_bit.local); | 3936 | try self.addLabel(.local_set, overflow_bit.local); |
| 3993 | } else if (int_info.signedness == .signed and op != .shl and op != .mul) { | 3937 | } else if (int_info.signedness == .signed and op != .shl) { |
| 3994 | // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow. | 3938 | // for overflow, we first check if lhs is > 0 (or lhs < 0 in case of subtraction). If not, we will not overflow. |
| 3995 | // We first create an outer block, where we handle overflow. | 3939 | // We first create an outer block, where we handle overflow. |
| 3996 | // Then we create an inner block, where underflow is handled. | 3940 | // Then we create an inner block, where underflow is handled. |
| ... | @@ -4038,64 +3982,100 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue | ... | @@ -4038,64 +3982,100 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 4038 | } | 3982 | } |
| 4039 | try self.addLabel(.local_set, tmp_val.local); | 3983 | try self.addLabel(.local_set, tmp_val.local); |
| 4040 | break :blk tmp_val; | 3984 | break :blk tmp_val; |
| 4041 | } else if (op == .mul) blk: { | 3985 | } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); |
| 4042 | // for 32 & 64 bitsize we calculate overflow | 3986 | |
| 4043 | // differently. | 3987 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4044 | if (int_info.bits == 32) { | 3988 | try self.store(result_ptr, bin_op, lhs_ty, 0); |
| 4045 | const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; | 3989 | const offset = @intCast(u32, lhs_ty.abiSize(self.target)); |
| 4046 | const lhs_upcast = try self.intcast(lhs, lhs_ty, new_ty); | 3990 | try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); |
| 4047 | const rhs_upcast = try self.intcast(rhs, lhs_ty, new_ty); | 3991 | |
| 4048 | const bin_op = try self.binOp(lhs_upcast, rhs_upcast, new_ty, op); | 3992 | return result_ptr; |
| 4049 | if (int_info.signedness == .unsigned) { | 3993 | } |
| 4050 | const shr = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); | 3994 | |
| 4051 | const wrap = try self.intcast(shr, new_ty, lhs_ty); | 3995 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4052 | const cmp_res = try self.cmp(wrap, zero, lhs_ty, .neq); | 3996 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 4053 | try self.emitWValue(cmp_res); | 3997 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 4054 | try self.addLabel(.local_set, overflow_bit.local); | 3998 | const lhs = try self.resolveInst(extra.lhs); |
| 4055 | break :blk try self.intcast(bin_op, new_ty, lhs_ty); | 3999 | const rhs = try self.resolveInst(extra.rhs); |
| 4056 | } else { | 4000 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 4057 | const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); | 4001 | |
| 4058 | const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); | 4002 | if (lhs_ty.zigTypeTag() == .Vector) { |
| 4059 | | 4003 | return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 4060 | const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); | 4004 | } |
| 4061 | const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); | 4005 | |
| 4062 | const cmp_res = try self.cmp(down_shr_res, shr, lhs_ty, .neq); | 4006 | // We store the bit if it's overflowed or not in this. As it's zero-initialized |
| 4063 | try self.emitWValue(cmp_res); | 4007 | // we only need to update it if an overflow (or underflow) occured. |
| 4064 | try self.addLabel(.local_set, overflow_bit.local); | 4008 | const overflow_bit = try self.allocLocal(Type.initTag(.u1)); |
| 4065 | break :blk down_cast; | 4009 | const int_info = lhs_ty.intInfo(self.target); |
| 4066 | } | 4010 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 4067 | } else if (int_info.signedness == .signed) { | 4011 | return self.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits}); |
| 4068 | const shift_imm = if (wasm_bits == 32) | 4012 | }; |
| 4069 | WValue{ .imm32 = wasm_bits - int_info.bits } | 4013 | |
| 4070 | else | 4014 | if (wasm_bits == 64) { |
| 4071 | WValue{ .imm64 = wasm_bits - int_info.bits }; | 4015 | return self.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits}); |
| 4072 | | 4016 | } |
| 4073 | const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl); | 4017 | |
| 4074 | const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr); | 4018 | const zero = switch (wasm_bits) { |
| 4075 | const rhs_shl = try self.binOp(rhs, shift_imm, lhs_ty, .shl); | 4019 | 32 => WValue{ .imm32 = 0 }, |
| 4076 | const rhs_shr = try self.binOp(rhs_shl, shift_imm, lhs_ty, .shr); | 4020 | 64 => WValue{ .imm64 = 0 }, |
| 4077 | | 4021 | else => unreachable, |
| 4078 | const bin_op = try self.binOp(lhs_shr, rhs_shr, lhs_ty, op); | 4022 | }; |
| 4079 | const shl = try self.binOp(bin_op, shift_imm, lhs_ty, .shl); | 4023 | |
| 4080 | const shr = try self.binOp(shl, shift_imm, lhs_ty, .shr); | 4024 | // for 32 bit integers we upcast it to a 64bit integer |
| 4081 | | 4025 | const bin_op = if (int_info.bits == 32) blk: { |
| 4082 | const cmp_op = try self.cmp(shr, bin_op, lhs_ty, .neq); | 4026 | const new_ty = if (int_info.signedness == .signed) Type.i64 else Type.u64; |
| 4083 | try self.emitWValue(cmp_op); | 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); |
| 4084 | try self.addLabel(.local_set, overflow_bit.local); | 4035 | try self.addLabel(.local_set, overflow_bit.local); |
| 4085 | break :blk try self.wrapOperand(bin_op, lhs_ty); | 4036 | break :blk try self.intcast(bin_op, new_ty, lhs_ty); |
| 4086 | } else { | 4037 | } else { |
| 4087 | const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); | 4038 | const down_cast = try self.intcast(bin_op, new_ty, lhs_ty); |
| 4088 | const shift_imm = if (wasm_bits == 32) | 4039 | const shr = try self.binOp(down_cast, .{ .imm32 = int_info.bits - 1 }, lhs_ty, .shr); |
| 4089 | WValue{ .imm32 = int_info.bits } | 4040 | |
| 4090 | else | 4041 | const shr_res = try self.binOp(bin_op, .{ .imm64 = int_info.bits }, new_ty, .shr); |
| 4091 | WValue{ .imm64 = int_info.bits }; | 4042 | const down_shr_res = try self.intcast(shr_res, new_ty, lhs_ty); |
| 4092 | const shr = try self.binOp(bin_op, shift_imm, lhs_ty, .shr); | 4043 | const cmp_res = try self.cmp(down_shr_res, shr, lhs_ty, .neq); |
| 4093 | const cmp_op = try self.cmp(shr, zero, lhs_ty, .neq); | 4044 | try self.emitWValue(cmp_res); |
| 4094 | try self.emitWValue(cmp_op); | | |
| 4095 | try self.addLabel(.local_set, overflow_bit.local); | 4045 | try self.addLabel(.local_set, overflow_bit.local); |
| 4096 | break :blk try self.wrapOperand(bin_op, lhs_ty); | 4046 | break :blk down_cast; |
| 4097 | } | 4047 | } |
| 4098 | } else try self.wrapBinOp(lhs, rhs, lhs_ty, op); | 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); |
| | 4065 | try self.addLabel(.local_set, overflow_bit.local); |
| | 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 | }; |
| 4099 | | 4079 | |
| 4100 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); | 4080 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4101 | try self.store(result_ptr, bin_op, lhs_ty, 0); | 4081 | try self.store(result_ptr, bin_op, lhs_ty, 0); |