authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-05-03 20:16:52+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-05-19 20:18:59+02:00
logd353d208e295a01d6f844ccdb7e641a94e6fcb11
treef7a34c5cc1d2365dd4c0dda226f24ddb93c89f4a
parent992de8e61718a1a77666e473dc37872afbb80c98
signaturelock-open Commit is signed but in an unrecognized format.

wasm: implement `@mulWithOverflow` for big ints

Currently we only support exact 128 bit *unsigned* integers

1 files changed, 61 insertions(+), 6 deletions(-)

src/arch/wasm/CodeGen.zig+61-6
...@@ -5550,16 +5550,12 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5550,16 +5550,12 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
55505550
5551 const int_info = lhs_ty.intInfo(func.target);5551 const int_info = lhs_ty.intInfo(func.target);
5552 const wasm_bits = toWasmBits(int_info.bits) orelse {5552 const wasm_bits = toWasmBits(int_info.bits) orelse {
5553 return func.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits});
5554 };
5555
5556 if (wasm_bits > 64) {
5557 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});5553 return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits});
5558 }5554 };
55595555
5560 const zero = switch (wasm_bits) {5556 const zero = switch (wasm_bits) {
5561 32 => WValue{ .imm32 = 0 },5557 32 => WValue{ .imm32 = 0 },
5562 64 => WValue{ .imm64 = 0 },5558 64, 128 => WValue{ .imm64 = 0 },
5563 else => unreachable,5559 else => unreachable,
5564 };5560 };
55655561
...@@ -5638,6 +5634,65 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5638,6 +5634,65 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5638 _ = try func.cmp(lsb, msb_shifted, lhs_ty, .neq);5634 _ = try func.cmp(lsb, msb_shifted, lhs_ty, .neq);
5639 try func.addLabel(.local_set, overflow_bit.local.value);5635 try func.addLabel(.local_set, overflow_bit.local.value);
5640 break :blk res;5636 break :blk res;
5637 } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: {
5638 var lhs_msb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64);
5639 defer lhs_msb.free(func);
5640 var lhs_lsb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64);
5641 defer lhs_lsb.free(func);
5642 var rhs_msb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64);
5643 defer rhs_msb.free(func);
5644 var rhs_lsb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64);
5645 defer rhs_lsb.free(func);
5646
5647 const mul1 = try func.callIntrinsic(
5648 "__multi3",
5649 &[_]Type{Type.i64} ** 4,
5650 Type.initTag(.i128),
5651 &.{ lhs_lsb, zero, rhs_msb, zero },
5652 );
5653 const mul2 = try func.callIntrinsic(
5654 "__multi3",
5655 &[_]Type{Type.i64} ** 4,
5656 Type.initTag(.i128),
5657 &.{ rhs_lsb, zero, lhs_msb, zero },
5658 );
5659 const mul3 = try func.callIntrinsic(
5660 "__multi3",
5661 &[_]Type{Type.i64} ** 4,
5662 Type.initTag(.i128),
5663 &.{ lhs_msb, zero, rhs_msb, zero },
5664 );
5665
5666 const rhs_lsb_not_zero = try func.cmp(rhs_lsb, zero, Type.u64, .neq);
5667 const lhs_lsb_not_zero = try func.cmp(lhs_lsb, zero, Type.u64, .neq);
5668 const lsb_and = try func.binOp(rhs_lsb_not_zero, lhs_lsb_not_zero, Type.bool, .@"and");
5669 const mul1_lsb = try func.load(mul1, Type.u64, 8);
5670 const mul1_lsb_not_zero = try func.cmp(mul1_lsb, zero, Type.u64, .neq);
5671 const lsb_or1 = try func.binOp(lsb_and, mul1_lsb_not_zero, Type.bool, .@"or");
5672 const mul2_lsb = try func.load(mul2, Type.u64, 8);
5673 const mul2_lsb_not_zero = try func.cmp(mul2_lsb, zero, Type.u64, .neq);
5674 const lsb_or = try func.binOp(lsb_or1, mul2_lsb_not_zero, Type.bool, .@"or");
5675
5676 const mul1_msb = try func.load(mul1, Type.u64, 0);
5677 const mul2_msb = try func.load(mul2, Type.u64, 0);
5678 const mul_add1 = try func.binOp(mul1_msb, mul2_msb, Type.u64, .add);
5679
5680 var mul3_lsb = try (try func.load(mul3, Type.u64, 8)).toLocal(func, Type.u64);
5681 defer mul3_lsb.free(func);
5682 var mul_add2 = try (try func.binOp(mul_add1, mul3_lsb, Type.u64, .add)).toLocal(func, Type.u64);
5683 defer mul_add2.free(func);
5684 const mul_add_lt = try func.cmp(mul_add2, mul3_lsb, Type.u64, .lt);
5685
5686 // result for overflow bit
5687 _ = try func.binOp(lsb_or, mul_add_lt, Type.bool, .@"or");
5688 try func.addLabel(.local_set, overflow_bit.local.value);
5689
5690 const tmp_result = try func.allocStack(Type.initTag(.u128));
5691 try func.emitWValue(tmp_result);
5692 const mul3_msb = try func.load(mul3, Type.u64, 0);
5693 try func.store(.stack, mul3_msb, Type.u64, tmp_result.offset());
5694 try func.store(tmp_result, mul_add2, Type.u64, 8);
5695 break :blk tmp_result;
5641 } else return func.fail("TODO: @mulWithOverflow for integers between 32 and 64 bits", .{});5696 } else return func.fail("TODO: @mulWithOverflow for integers between 32 and 64 bits", .{});
5642 var bin_op_local = try bin_op.toLocal(func, lhs_ty);5697 var bin_op_local = try bin_op.toLocal(func, lhs_ty);
5643 defer bin_op_local.free(func);5698 defer bin_op_local.free(func);