authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-12 21:53:57+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-18 07:43:33+02:00
log03a3ea2c1577208311d85482faadac6361442971
tree4263a470f5091f3f64fa45ec4e2db1ff1402b77a
parent167d3089ea11a8f0a45d6e083b1dacb107483b04

wasm: 128 bit intcast and binary operations

Also fixes some bugs in 128-bit binary comparisons where we checked if the lsb were equal, rather than msb.

1 files changed, 55 insertions(+), 39 deletions(-)

src/arch/wasm/CodeGen.zig+55-39
......@@ -1958,25 +1958,31 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
19581958 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
19591959 const lhs = try self.resolveInst(bin_op.lhs);
19601960 const rhs = try self.resolveInst(bin_op.rhs);
1961 const operand_ty = self.air.typeOfIndex(inst);
19621961 const ty = self.air.typeOf(bin_op.lhs);
19631962
1964 if (isByRef(operand_ty, self.target)) {
1965 return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty.fmtDebug()});
1966 }
1967
19681963 return self.binOp(lhs, rhs, ty, op);
19691964}
19701965
19711966fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
1972 try self.emitWValue(lhs);
1973 try self.emitWValue(rhs);
1967 if (isByRef(ty, self.target)) {
1968 if (ty.zigTypeTag() == .Int) {
1969 return self.binOpBigInt(lhs, rhs, ty, op);
1970 } else {
1971 return self.fail(
1972 "TODO: Implement binary operation for type: {}",
1973 .{ty.fmt(self.bin_file.base.options.module.?)},
1974 );
1975 }
1976 }
19741977
19751978 const opcode: wasm.Opcode = buildOpcode(.{
19761979 .op = op,
19771980 .valtype1 = typeToValtype(ty, self.target),
19781981 .signedness = if (ty.isSignedInt()) .signed else .unsigned,
19791982 });
1983 try self.emitWValue(lhs);
1984 try self.emitWValue(rhs);
1985
19801986 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
19811987
19821988 // save the result in a temporary
......@@ -1985,6 +1991,37 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa
19851991 return bin_local;
19861992}
19871993
1994fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
1995 if (ty.intInfo(self.target).bits != 128) {
1996 return self.fail("TODO: Implement binary operation for big integer", .{});
1997 }
1998
1999 if (op != .add and op != .sub) {
2000 return self.fail("TODO: Implement binary operation for big integers", .{});
2001 }
2002
2003 const result = try self.allocStack(ty);
2004 const lhs_high_bit = try self.load(lhs, Type.u64, 0);
2005 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
2006 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
2007 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
2008
2009 const low_op_res = try self.binOp(rhs_low_bit, lhs_low_bit, Type.u64, op);
2010 const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);
2011
2012 const lt = if (op == .add) blk: {
2013 break :blk try self.cmp(high_op_res, rhs_high_bit, Type.u64, .lt);
2014 } else if (op == .sub) blk: {
2015 break :blk try self.cmp(rhs_high_bit, lhs_high_bit, Type.u64, .lt);
2016 } else unreachable;
2017 const tmp = try self.intcast(lt, Type.u32, Type.u64);
2018 const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op);
2019
2020 try self.store(result, high_op_res, Type.u64, 0);
2021 try self.store(result, tmp_op, Type.u64, 8);
2022 return result;
2023}
2024
19882025fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
19892026 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
19902027 const lhs = try self.resolveInst(bin_op.lhs);
......@@ -1993,10 +2030,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
19932030 const ty = self.air.typeOf(bin_op.lhs);
19942031 if (ty.zigTypeTag() == .Vector) {
19952032 return self.fail("TODO: Implement wrapping arithmetic for vectors", .{});
1996 } else if (ty.abiSize(self.target) > 8 and op == .mul) {
1997 return self.fail("TODO: Implement wrapping multiplication for bitsize > 64", .{});
1998 } else if (ty.abiSize(self.target) > 16) {
1999 return self.fail("TODO: Implement wrapping arithmetic for bitsize > 128", .{});
20002033 }
20012034
20022035 return self.wrapBinOp(lhs, rhs, ty, op);
......@@ -2007,30 +2040,9 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError
20072040 var wasm_bits = toWasmBits(bit_size) orelse {
20082041 return self.fail("TODO: Implement wrapping arithmetic for integers with bitsize: {d}\n", .{bit_size});
20092042 };
2043
20102044 if (wasm_bits == 128) {
2011 if (op == .mul) return self.fail("TODO: Implement wrapping multiplication for 128bit integers", .{});
2012 if (bit_size != wasm_bits) return self.fail("TODO: Implement wrapping arithmetic for integers > 64 & < 128", .{});
2013
2014 const result = try self.allocStack(ty);
2015 const lhs_high_bit = try self.load(lhs, Type.u64, 0);
2016 const lhs_low_bit = try self.load(lhs, Type.u64, 8);
2017 const rhs_high_bit = try self.load(rhs, Type.u64, 0);
2018 const rhs_low_bit = try self.load(rhs, Type.u64, 8);
2019
2020 const low_op_res = try self.binOp(rhs_low_bit, lhs_low_bit, Type.u64, op);
2021 const high_op_res = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op);
2022
2023 const lt = if (op == .add) blk: {
2024 break :blk try self.binOp(high_op_res, rhs_high_bit, Type.u64, .lt);
2025 } else if (op == .sub) blk: {
2026 break :blk try self.binOp(rhs_high_bit, lhs_high_bit, Type.u64, .lt);
2027 } else unreachable;
2028 const tmp = try self.intcast(lt, Type.u32, Type.u64);
2029 const tmp_op = try self.binOp(low_op_res, tmp, Type.u64, op);
2030
2031 try self.store(result, high_op_res, Type.u64, 0);
2032 try self.store(result, tmp_op, Type.u64, 8);
2033 return result;
2045 return self.binOp(lhs, rhs, ty, op);
20342046 }
20352047
20362048 const opcode: wasm.Opcode = buildOpcode(.{
......@@ -2044,6 +2056,10 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError
20442056 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
20452057 const bin_local = try self.allocLocal(ty);
20462058 try self.addLabel(.local_set, bin_local.local);
2059 if (wasm_bits == bit_size) {
2060 return bin_local;
2061 }
2062
20472063 return self.wrapOperand(bin_local, ty);
20482064}
20492065
......@@ -2994,7 +3010,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
29943010 try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
29953011 }
29963012 return stack_ptr;
2997 } else return self.fail("todo Wasm @intCast to 128bit integers", .{});
3013 } else return self.load(operand, wanted, 0);
29983014
29993015 const result = try self.allocLocal(wanted);
30003016 try self.addLabel(.local_set, result.local);
......@@ -3760,20 +3776,20 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma
37603776 const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or");
37613777
37623778 switch (op) {
3763 .eq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .eq),
3764 .neq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .neq),
3779 .eq => return self.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .eq),
3780 .neq => return self.cmp(or_result, .{ .imm64 = 0 }, Type.u64, .neq),
37653781 else => unreachable,
37663782 }
37673783 },
37683784 else => {
37693785 const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64;
3770 const low_bit_eql = try self.cmp(lhs_low_bit, rhs_low_bit, ty, .eq);
3786 const high_bit_eql = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq);
37713787 const high_bit_cmp = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op);
37723788 const low_bit_cmp = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op);
37733789
37743790 try self.emitWValue(low_bit_cmp);
37753791 try self.emitWValue(high_bit_cmp);
3776 try self.emitWValue(low_bit_eql);
3792 try self.emitWValue(high_bit_eql);
37773793 try self.addTag(.select);
37783794 },
37793795 }