| ... | @@ -1993,22 +1993,54 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | ... | @@ -1993,22 +1993,54 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1993 | const ty = self.air.typeOf(bin_op.lhs); | 1993 | const ty = self.air.typeOf(bin_op.lhs); |
| 1994 | if (ty.zigTypeTag() == .Vector) { | 1994 | if (ty.zigTypeTag() == .Vector) { |
| 1995 | return self.fail("TODO: Implement wrapping arithmetic for vectors", .{}); | 1995 | return self.fail("TODO: Implement wrapping arithmetic for vectors", .{}); |
| 1996 | } else if (ty.abiSize(self.target) > 8) { | 1996 | } else if (ty.abiSize(self.target) > 8 and op == .mul) { |
| 1997 | return self.fail("TODO: Implement wrapping arithmetic for bitsize > 64", .{}); | 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", .{}); |
| 1998 | } | 2000 | } |
| 1999 | | 2001 | |
| 2000 | return self.wrapBinOp(lhs, rhs, ty, op); | 2002 | return self.wrapBinOp(lhs, rhs, ty, op); |
| 2001 | } | 2003 | } |
| 2002 | | 2004 | |
| 2003 | fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { | 2005 | fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 2004 | try self.emitWValue(lhs); | 2006 | const bit_size = ty.intInfo(self.target).bits; |
| 2005 | try self.emitWValue(rhs); | 2007 | var wasm_bits = toWasmBits(bit_size) orelse { |
| | 2008 | return self.fail("TODO: Implement wrapping arithmetic for integers with bitsize: {d}\n", .{bit_size}); |
| | 2009 | }; |
| | 2010 | 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; |
| | 2034 | } |
| 2006 | | 2035 | |
| 2007 | const opcode: wasm.Opcode = buildOpcode(.{ | 2036 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 2008 | .op = op, | 2037 | .op = op, |
| 2009 | .valtype1 = typeToValtype(ty, self.target), | 2038 | .valtype1 = typeToValtype(ty, self.target), |
| 2010 | .signedness = if (ty.isSignedInt()) .signed else .unsigned, | 2039 | .signedness = if (ty.isSignedInt()) .signed else .unsigned, |
| 2011 | }); | 2040 | }); |
| | 2041 | |
| | 2042 | try self.emitWValue(lhs); |
| | 2043 | try self.emitWValue(rhs); |
| 2012 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 2044 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2013 | const bin_local = try self.allocLocal(ty); | 2045 | const bin_local = try self.allocLocal(ty); |
| 2014 | try self.addLabel(.local_set, bin_local.local); | 2046 | try self.addLabel(.local_set, bin_local.local); |
| ... | @@ -3721,7 +3753,6 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma | ... | @@ -3721,7 +3753,6 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma |
| 3721 | const rhs_high_bit = try self.load(rhs, Type.u64, 0); | 3753 | const rhs_high_bit = try self.load(rhs, Type.u64, 0); |
| 3722 | const rhs_low_bit = try self.load(rhs, Type.u64, 8); | 3754 | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| 3723 | | 3755 | |
| 3724 | try self.emitWValue(or_result); | | |
| 3725 | switch (op) { | 3756 | switch (op) { |
| 3726 | .eq, .neq => { | 3757 | .eq, .neq => { |
| 3727 | const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor); | 3758 | const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor); |
| ... | @@ -3729,7 +3760,7 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma | ... | @@ -3729,7 +3760,7 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma |
| 3729 | const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or"); | 3760 | const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or"); |
| 3730 | | 3761 | |
| 3731 | switch (op) { | 3762 | switch (op) { |
| 3732 | .eq => try self.addTag(.i64_eqz), | 3763 | .eq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .eq), |
| 3733 | .neq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .neq), | 3764 | .neq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .neq), |
| 3734 | else => unreachable, | 3765 | else => unreachable, |
| 3735 | } | 3766 | } |