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.
This commit is contained in:
Luuk de Gram 2023-04-29 17:02:50 +02:00
parent eb77e3381f
commit 8236a26c60
No known key found for this signature in database
GPG Key ID: A8CFE58E4DC7D664

View File

@ -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 {