From 8236a26c605c8c4276f2062b3b6f55497cd45e53 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sat, 29 Apr 2023 17:02:50 +0200 Subject: [PATCH] wasm: implement mul, shl and xor for big ints Uses compiler-rt for multiplication and shifting left, while lowers it down using regular instructions for xor. --- src/arch/wasm/CodeGen.zig | 74 ++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 3ae7f639fe..4296cc557f 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2565,37 +2565,55 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { if (ty.intInfo(func.target).bits > 128) { - return func.fail("TODO: Implement binary operation for big integer", .{}); + return func.fail("TODO: Implement binary operation for big integers larger than 128 bits", .{}); } - if (op != .add and op != .sub) { - return func.fail("TODO: Implement binary operation for big integers", .{}); + switch (op) { + .mul => return func.callIntrinsic("__multi3", &.{ ty, ty }, ty, &.{ lhs, rhs }), + .shr => return func.callIntrinsic("__lshrti3", &.{ ty, Type.i32 }, ty, &.{ lhs, rhs }), + .xor => { + const result = try func.allocStack(ty); + try func.emitWValue(result); + const lhs_high_bit = try func.load(lhs, Type.u64, 0); + const rhs_high_bit = try func.load(rhs, Type.u64, 0); + const xor_high_bit = try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor); + try func.store(.stack, xor_high_bit, Type.u64, result.offset()); + + try func.emitWValue(result); + const lhs_low_bit = try func.load(lhs, Type.u64, 8); + const rhs_low_bit = try func.load(rhs, Type.u64, 8); + const xor_low_bit = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); + try func.store(.stack, xor_low_bit, Type.u64, result.offset() + 8); + return result; + }, + .add, .sub => { + const result = try func.allocStack(ty); + var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64); + defer lhs_high_bit.free(func); + var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); + defer rhs_high_bit.free(func); + var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64); + defer high_op_res.free(func); + + const lhs_low_bit = try func.load(lhs, Type.u64, 8); + const rhs_low_bit = try func.load(rhs, Type.u64, 8); + const low_op_res = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); + + const lt = if (op == .add) blk: { + break :blk try func.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); + } else if (op == .sub) blk: { + break :blk try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); + } else unreachable; + const tmp = try func.intcast(lt, Type.u32, Type.u64); + var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64); + defer tmp_op.free(func); + + try func.store(result, high_op_res, Type.u64, 0); + try func.store(result, tmp_op, Type.u64, 8); + return result; + }, + else => return func.fail("TODO: Implement binary operation for big integers: '{s}'", .{@tagName(op)}), } - - const result = try func.allocStack(ty); - var lhs_high_bit = try (try func.load(lhs, Type.u64, 0)).toLocal(func, Type.u64); - defer lhs_high_bit.free(func); - var rhs_high_bit = try (try func.load(rhs, Type.u64, 0)).toLocal(func, Type.u64); - defer rhs_high_bit.free(func); - var high_op_res = try (try func.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(func, Type.u64); - defer high_op_res.free(func); - - const lhs_low_bit = try func.load(lhs, Type.u64, 8); - const rhs_low_bit = try func.load(rhs, Type.u64, 8); - const low_op_res = try func.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); - - const lt = if (op == .add) blk: { - break :blk try func.cmp(high_op_res, rhs_high_bit, Type.u64, .lt); - } else if (op == .sub) blk: { - break :blk try func.cmp(lhs_high_bit, rhs_high_bit, Type.u64, .lt); - } else unreachable; - const tmp = try func.intcast(lt, Type.u32, Type.u64); - var tmp_op = try (try func.binOp(low_op_res, tmp, Type.u64, op)).toLocal(func, Type.u64); - defer tmp_op.free(func); - - try func.store(result, high_op_res, Type.u64, 0); - try func.store(result, tmp_op, Type.u64, 8); - return result; } const FloatOp = enum {