| ... | @@ -3801,6 +3801,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std | ... | @@ -3801,6 +3801,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std |
| 3801 | /// Compares big integers by checking both its high bits and low bits. | 3801 | /// Compares big integers by checking both its high bits and low bits. |
| 3802 | /// TODO: Lower this to compiler_rt call when bitsize > 128 | 3802 | /// TODO: Lower this to compiler_rt call when bitsize > 128 |
| 3803 | fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { | 3803 | fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { |
| | 3804 | assert(operand_ty.abiSize(self.target) >= 16); |
| 3804 | if (operand_ty.intInfo(self.target).bits > 128) { | 3805 | if (operand_ty.intInfo(self.target).bits > 128) { |
| 3805 | return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); | 3806 | return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); |
| 3806 | } | 3807 | } |
| ... | @@ -4093,6 +4094,10 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W | ... | @@ -4093,6 +4094,10 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W |
| 4093 | return self.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits}); | 4094 | return self.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits}); |
| 4094 | }; | 4095 | }; |
| 4095 | | 4096 | |
| | 4097 | if (wasm_bits == 128) { |
| | 4098 | return self.airAddSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, self.air.typeOfIndex(inst), op); |
| | 4099 | } |
| | 4100 | |
| 4096 | const zero = switch (wasm_bits) { | 4101 | const zero = switch (wasm_bits) { |
| 4097 | 32 => WValue{ .imm32 = 0 }, | 4102 | 32 => WValue{ .imm32 = 0 }, |
| 4098 | 64 => WValue{ .imm64 = 0 }, | 4103 | 64 => WValue{ .imm64 = 0 }, |
| ... | @@ -4144,6 +4149,64 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W | ... | @@ -4144,6 +4149,64 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W |
| 4144 | return result_ptr; | 4149 | return result_ptr; |
| 4145 | } | 4150 | } |
| 4146 | | 4151 | |
| | 4152 | fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue { |
| | 4153 | assert(op == .add or op == .sub); |
| | 4154 | const int_info = ty.intInfo(self.target); |
| | 4155 | const is_signed = int_info.signedness == .signed; |
| | 4156 | if (int_info.bits != 128) { |
| | 4157 | return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits}); |
| | 4158 | } |
| | 4159 | |
| | 4160 | const lhs_high_bit = try self.load(lhs, Type.u64, 0); |
| | 4161 | const lhs_low_bit = try self.load(lhs, Type.u64, 8); |
| | 4162 | const rhs_high_bit = try self.load(rhs, Type.u64, 0); |
| | 4163 | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| | 4164 | |
| | 4165 | const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); |
| | 4166 | const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op); |
| | 4167 | |
| | 4168 | const lt = if (op == .add) blk: { |
| | 4169 | break :blk try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt); |
| | 4170 | } else if (op == .sub) blk: { |
| | 4171 | break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); |
| | 4172 | } else unreachable; |
| | 4173 | const tmp = try self.intcast(lt, Type.u32, Type.u64); |
| | 4174 | const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op); |
| | 4175 | |
| | 4176 | const overflow_bit = if (is_signed) blk: { |
| | 4177 | const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor); |
| | 4178 | const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); |
| | 4179 | const to_wrap = if (op == .add) wrap: { |
| | 4180 | break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); |
| | 4181 | } else xor_low; |
| | 4182 | const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and"); |
| | 4183 | break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed |
| | 4184 | } else blk: { |
| | 4185 | const eq = try self.cmp(tmp_op, lhs_low_bit, Type.u64, .eq); |
| | 4186 | const op_eq = try self.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt); |
| | 4187 | |
| | 4188 | const first_arg = if (op == .sub) arg: { |
| | 4189 | break :arg try self.cmp(high_op_res, lhs_high_bit, Type.u64, .gt); |
| | 4190 | } else lt; |
| | 4191 | |
| | 4192 | try self.emitWValue(first_arg); |
| | 4193 | try self.emitWValue(op_eq); |
| | 4194 | try self.emitWValue(eq); |
| | 4195 | try self.addTag(.select); |
| | 4196 | |
| | 4197 | const overflow_bit = try self.allocLocal(Type.initTag(.u1)); |
| | 4198 | try self.addLabel(.local_set, overflow_bit.local); |
| | 4199 | break :blk overflow_bit; |
| | 4200 | }; |
| | 4201 | |
| | 4202 | const result_ptr = try self.allocStack(result_ty); |
| | 4203 | try self.store(result_ptr, high_op_res, Type.u64, 0); |
| | 4204 | try self.store(result_ptr, tmp_op, Type.u64, 8); |
| | 4205 | try self.store(result_ptr, overflow_bit, Type.initTag(.u1), 16); |
| | 4206 | |
| | 4207 | return result_ptr; |
| | 4208 | } |
| | 4209 | |
| 4147 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 4210 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4148 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 4211 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 4149 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 4212 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |