diff options
| author | Luuk de Gram <luuk@degram.dev> | 2022-05-17 21:56:11 +0200 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2022-05-18 07:43:33 +0200 |
| commit | ed25ce77f58cd0ddfd5e3f5412c4cb66d1cc1331 (patch) | |
| tree | ca9e9208fa7ed2bb42c11f2dcfb224bbc3098c27 /src | |
| parent | fd081c74f10de71eaf00b0ccd7c4e4a0c0c5c3ad (diff) | |
| download | zig-ed25ce77f58cd0ddfd5e3f5412c4cb66d1cc1331.tar.gz zig-ed25ce77f58cd0ddfd5e3f5412c4cb66d1cc1331.zip | |
wasm: Implement {add/sub}WithOverflow for 128bit
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 63 |
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; |
