aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLuuk de Gram <luuk@degram.dev>2022-05-17 21:56:11 +0200
committerLuuk de Gram <luuk@degram.dev>2022-05-18 07:43:33 +0200
commited25ce77f58cd0ddfd5e3f5412c4cb66d1cc1331 (patch)
treeca9e9208fa7ed2bb42c11f2dcfb224bbc3098c27 /src
parentfd081c74f10de71eaf00b0ccd7c4e4a0c0c5c3ad (diff)
downloadzig-ed25ce77f58cd0ddfd5e3f5412c4cb66d1cc1331.tar.gz
zig-ed25ce77f58cd0ddfd5e3f5412c4cb66d1cc1331.zip
wasm: Implement {add/sub}WithOverflow for 128bit
Diffstat (limited to 'src')
-rw-r--r--src/arch/wasm/CodeGen.zig63
1 files changed, 63 insertions, 0 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index feaa1cf145..60d03ccafd 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -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;