| ... | ... | @@ -1450,8 +1450,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1450 | 1450 | .min => self.airMaxMin(inst, .min), |
| 1451 | 1451 | .mul_add => self.airMulAdd(inst), |
| 1452 | 1452 | |
| 1453 | | .add_with_overflow => self.airBinOpOverflow(inst, .add), |
| 1454 | | .sub_with_overflow => self.airBinOpOverflow(inst, .sub), |
| 1453 | .add_with_overflow => self.airAddSubWithOverflow(inst, .add), |
| 1454 | .sub_with_overflow => self.airAddSubWithOverflow(inst, .sub), |
| 1455 | 1455 | .shl_with_overflow => self.airBinOpOverflow(inst, .shl), |
| 1456 | 1456 | .mul_with_overflow => self.airMulWithOverflow(inst), |
| 1457 | 1457 | |
| ... | ... | @@ -3988,7 +3988,7 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 3988 | 3988 | const cmp_res = try self.cmp(rhs, diff, lhs_ty, .gt); |
| 3989 | 3989 | try self.emitWValue(cmp_res); |
| 3990 | 3990 | try self.addLabel(.local_set, overflow_bit.local); |
| 3991 | | } else if (int_info.signedness == .unsigned and op == .sub) { |
| 3991 | } else if (op == .sub) { |
| 3992 | 3992 | const cmp_res = try self.cmp(lhs, rhs, lhs_ty, .lt); |
| 3993 | 3993 | try self.emitWValue(cmp_res); |
| 3994 | 3994 | try self.addLabel(.local_set, overflow_bit.local); |
| ... | ... | @@ -4050,6 +4050,79 @@ fn airBinOpOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue |
| 4050 | 4050 | return result_ptr; |
| 4051 | 4051 | } |
| 4052 | 4052 | |
| 4053 | fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 4054 | assert(op == .add or op == .sub); |
| 4055 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 4056 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 4057 | const lhs_op = try self.resolveInst(extra.lhs); |
| 4058 | const rhs_op = try self.resolveInst(extra.rhs); |
| 4059 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 4060 | |
| 4061 | if (lhs_ty.zigTypeTag() == .Vector) { |
| 4062 | return self.fail("TODO: Implement overflow arithmetic for vectors", .{}); |
| 4063 | } |
| 4064 | |
| 4065 | const int_info = lhs_ty.intInfo(self.target); |
| 4066 | const is_signed = int_info.signedness == .signed; |
| 4067 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 4068 | return self.fail("TODO: Implement sub_with_overflow for integer bitsize: {d}", .{int_info.bits}); |
| 4069 | }; |
| 4070 | |
| 4071 | if (wasm_bits == 128) { |
| 4072 | return self.fail("TODO: Implement sub_with_overflow for 128 bit integers", .{}); |
| 4073 | } |
| 4074 | |
| 4075 | const zero = switch (wasm_bits) { |
| 4076 | 32 => WValue{ .imm32 = 0 }, |
| 4077 | 64 => WValue{ .imm64 = 0 }, |
| 4078 | else => unreachable, |
| 4079 | }; |
| 4080 | const shift_amt = wasm_bits - int_info.bits; |
| 4081 | const shift_val = switch (wasm_bits) { |
| 4082 | 32 => WValue{ .imm32 = shift_amt }, |
| 4083 | 64 => WValue{ .imm64 = shift_amt }, |
| 4084 | else => unreachable, |
| 4085 | }; |
| 4086 | |
| 4087 | // for signed integers, we first apply signed shifts by the difference in bits |
| 4088 | // to get the signed value, as we store it internally as 2's complement. |
| 4089 | const lhs = if (wasm_bits != int_info.bits and is_signed) blk: { |
| 4090 | const shl = try self.binOp(lhs_op, shift_val, lhs_ty, .shl); |
| 4091 | break :blk try self.binOp(shl, shift_val, lhs_ty, .shr); |
| 4092 | } else lhs_op; |
| 4093 | const rhs = if (wasm_bits != int_info.bits and is_signed) blk: { |
| 4094 | const shl = try self.binOp(rhs_op, shift_val, lhs_ty, .shl); |
| 4095 | break :blk try self.binOp(shl, shift_val, lhs_ty, .shr); |
| 4096 | } else rhs_op; |
| 4097 | |
| 4098 | const bin_op = try self.binOp(lhs, rhs, lhs_ty, op); |
| 4099 | const result = if (wasm_bits != int_info.bits) blk: { |
| 4100 | break :blk try self.wrapOperand(bin_op, lhs_ty); |
| 4101 | } else bin_op; |
| 4102 | |
| 4103 | const cmp_op: std.math.CompareOperator = if (op == .sub) .gt else .lt; |
| 4104 | const overflow_bit: WValue = if (is_signed) blk: { |
| 4105 | if (wasm_bits == int_info.bits) { |
| 4106 | const cmp_zero = try self.cmp(rhs, zero, lhs_ty, cmp_op); |
| 4107 | const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt); |
| 4108 | break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); // result of cmp_zero and lt is always 32bit |
| 4109 | } |
| 4110 | const shl = try self.binOp(bin_op, shift_val, lhs_ty, .shl); |
| 4111 | const shr = try self.binOp(shl, shift_val, lhs_ty, .shr); |
| 4112 | break :blk try self.cmp(shr, bin_op, lhs_ty, .neq); |
| 4113 | } else if (wasm_bits == int_info.bits) |
| 4114 | try self.cmp(bin_op, lhs, lhs_ty, cmp_op) |
| 4115 | else |
| 4116 | try self.cmp(bin_op, result, lhs_ty, .neq); |
| 4117 | |
| 4118 | const result_ptr = try self.allocStack(self.air.typeOfIndex(inst)); |
| 4119 | try self.store(result_ptr, result, lhs_ty, 0); |
| 4120 | const offset = @intCast(u32, lhs_ty.abiSize(self.target)); |
| 4121 | try self.store(result_ptr, overflow_bit, Type.initTag(.u1), offset); |
| 4122 | |
| 4123 | return result_ptr; |
| 4124 | } |
| 4125 | |
| 4053 | 4126 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4054 | 4127 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 4055 | 4128 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |