wasm: Implement {add/sub}WithOverflow for 128bit

This commit is contained in:
Luuk de Gram 2022-05-17 21:56:11 +02:00
parent fd081c74f1
commit ed25ce77f5

View File

@ -3801,6 +3801,7 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
/// Compares big integers by checking both its high bits and low bits.
/// TODO: Lower this to compiler_rt call when bitsize > 128
fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
assert(operand_ty.abiSize(self.target) >= 16);
if (operand_ty.intInfo(self.target).bits > 128) {
return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits});
}
@ -4093,6 +4094,10 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
return self.fail("TODO: Implement {{add/sub}}_with_overflow for integer bitsize: {d}", .{int_info.bits});
};
if (wasm_bits == 128) {
return self.airAddSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, self.air.typeOfIndex(inst), op);
}
const zero = switch (wasm_bits) {
32 => WValue{ .imm32 = 0 },
64 => WValue{ .imm64 = 0 },
@ -4144,6 +4149,64 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
return result_ptr;
}
fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue {
assert(op == .add or op == .sub);
const int_info = ty.intInfo(self.target);
const is_signed = int_info.signedness == .signed;
if (int_info.bits != 128) {
return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits});
}
const lhs_high_bit = try self.load(lhs, Type.u64, 0);
const lhs_low_bit = try self.load(lhs, Type.u64, 8);
const rhs_high_bit = try self.load(rhs, Type.u64, 0);
const rhs_low_bit = try self.load(rhs, Type.u64, 8);
const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op);
const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);
const lt = if (op == .add) blk: {
break :blk try self.cmp(high_op_res, lhs_high_bit, Type.u64, .lt);
} else if (op == .sub) blk: {
break :blk try self.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt);
} else unreachable;
const tmp = try self.intcast(lt, Type.u32, Type.u64);
const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op);
const overflow_bit = if (is_signed) blk: {
const xor_op = try self.binOp(lhs_low_bit, tmp_op, Type.u64, .xor);
const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
const to_wrap = if (op == .add) wrap: {
break :wrap try self.binOp(xor_low, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
} else xor_low;
const wrap = try self.binOp(to_wrap, xor_op, Type.u64, .@"and");
break :blk try self.cmp(wrap, .{ .imm64 = 0 }, Type.i64, .lt); // i64 because signed
} else blk: {
const eq = try self.cmp(tmp_op, lhs_low_bit, Type.u64, .eq);
const op_eq = try self.cmp(tmp_op, lhs_low_bit, Type.u64, if (op == .add) .lt else .gt);
const first_arg = if (op == .sub) arg: {
break :arg try self.cmp(high_op_res, lhs_high_bit, Type.u64, .gt);
} else lt;
try self.emitWValue(first_arg);
try self.emitWValue(op_eq);
try self.emitWValue(eq);
try self.addTag(.select);
const overflow_bit = try self.allocLocal(Type.initTag(.u1));
try self.addLabel(.local_set, overflow_bit.local);
break :blk overflow_bit;
};
const result_ptr = try self.allocStack(result_ty);
try self.store(result_ptr, high_op_res, Type.u64, 0);
try self.store(result_ptr, tmp_op, Type.u64, 8);
try self.store(result_ptr, overflow_bit, Type.initTag(.u1), 16);
return result_ptr;
}
fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;