diff options
| author | Luuk de Gram <luuk@degram.dev> | 2023-05-03 20:16:52 +0200 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2023-05-19 20:18:59 +0200 |
| commit | d353d208e295a01d6f844ccdb7e641a94e6fcb11 (patch) | |
| tree | f7a34c5cc1d2365dd4c0dda226f24ddb93c89f4a /src/arch/wasm/CodeGen.zig | |
| parent | 992de8e61718a1a77666e473dc37872afbb80c98 (diff) | |
| download | zig-d353d208e295a01d6f844ccdb7e641a94e6fcb11.tar.gz zig-d353d208e295a01d6f844ccdb7e641a94e6fcb11.zip | |
wasm: implement `@mulWithOverflow` for big ints
Currently we only support exact 128 bit *unsigned* integers
Diffstat (limited to 'src/arch/wasm/CodeGen.zig')
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 67 |
1 files changed, 61 insertions, 6 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 53c537c463..afe66c504e 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -5550,16 +5550,12 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const int_info = lhs_ty.intInfo(func.target); const wasm_bits = toWasmBits(int_info.bits) orelse { - return func.fail("TODO: Implement overflow arithmetic for integer bitsize: {d}", .{int_info.bits}); - }; - - if (wasm_bits > 64) { return func.fail("TODO: Implement `@mulWithOverflow` for integer bitsize: {d}", .{int_info.bits}); - } + }; const zero = switch (wasm_bits) { 32 => WValue{ .imm32 = 0 }, - 64 => WValue{ .imm64 = 0 }, + 64, 128 => WValue{ .imm64 = 0 }, else => unreachable, }; @@ -5638,6 +5634,65 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { _ = try func.cmp(lsb, msb_shifted, lhs_ty, .neq); try func.addLabel(.local_set, overflow_bit.local.value); break :blk res; + } else if (int_info.bits == 128 and int_info.signedness == .unsigned) blk: { + var lhs_msb = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64); + defer lhs_msb.free(func); + var lhs_lsb = try (try func.load(lhs, Type.u64, 8)).toLocal(func, Type.u64); + defer lhs_lsb.free(func); + var rhs_msb = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); + defer rhs_msb.free(func); + var rhs_lsb = try (try func.load(rhs, Type.u64, 8)).toLocal(func, Type.u64); + defer rhs_lsb.free(func); + + const mul1 = try func.callIntrinsic( + "__multi3", + &[_]Type{Type.i64} ** 4, + Type.initTag(.i128), + &.{ lhs_lsb, zero, rhs_msb, zero }, + ); + const mul2 = try func.callIntrinsic( + "__multi3", + &[_]Type{Type.i64} ** 4, + Type.initTag(.i128), + &.{ rhs_lsb, zero, lhs_msb, zero }, + ); + const mul3 = try func.callIntrinsic( + "__multi3", + &[_]Type{Type.i64} ** 4, + Type.initTag(.i128), + &.{ lhs_msb, zero, rhs_msb, zero }, + ); + + const rhs_lsb_not_zero = try func.cmp(rhs_lsb, zero, Type.u64, .neq); + const lhs_lsb_not_zero = try func.cmp(lhs_lsb, zero, Type.u64, .neq); + const lsb_and = try func.binOp(rhs_lsb_not_zero, lhs_lsb_not_zero, Type.bool, .@"and"); + const mul1_lsb = try func.load(mul1, Type.u64, 8); + const mul1_lsb_not_zero = try func.cmp(mul1_lsb, zero, Type.u64, .neq); + const lsb_or1 = try func.binOp(lsb_and, mul1_lsb_not_zero, Type.bool, .@"or"); + const mul2_lsb = try func.load(mul2, Type.u64, 8); + const mul2_lsb_not_zero = try func.cmp(mul2_lsb, zero, Type.u64, .neq); + const lsb_or = try func.binOp(lsb_or1, mul2_lsb_not_zero, Type.bool, .@"or"); + + const mul1_msb = try func.load(mul1, Type.u64, 0); + const mul2_msb = try func.load(mul2, Type.u64, 0); + const mul_add1 = try func.binOp(mul1_msb, mul2_msb, Type.u64, .add); + + var mul3_lsb = try (try func.load(mul3, Type.u64, 8)).toLocal(func, Type.u64); + defer mul3_lsb.free(func); + var mul_add2 = try (try func.binOp(mul_add1, mul3_lsb, Type.u64, .add)).toLocal(func, Type.u64); + defer mul_add2.free(func); + const mul_add_lt = try func.cmp(mul_add2, mul3_lsb, Type.u64, .lt); + + // result for overflow bit + _ = try func.binOp(lsb_or, mul_add_lt, Type.bool, .@"or"); + try func.addLabel(.local_set, overflow_bit.local.value); + + const tmp_result = try func.allocStack(Type.initTag(.u128)); + try func.emitWValue(tmp_result); + const mul3_msb = try func.load(mul3, Type.u64, 0); + try func.store(.stack, mul3_msb, Type.u64, tmp_result.offset()); + try func.store(tmp_result, mul_add2, Type.u64, 8); + break :blk tmp_result; } else return func.fail("TODO: @mulWithOverflow for integers between 32 and 64 bits", .{}); var bin_op_local = try bin_op.toLocal(func, lhs_ty); defer bin_op_local.free(func); |
