Commit 8236a26c60

Luuk de Gram <luuk@degram.dev>
2023-04-29 17:02:50
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.
1 parent eb77e33
Changed files (1)
src
arch
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", .{});
-    }
-
-    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);
+    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());
 
-    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.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);
 
-    try func.store(result, high_op_res, Type.u64, 0);
-    try func.store(result, tmp_op, Type.u64, 8);
-    return result;
+            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 FloatOp = enum {