| ... | ... | @@ -1958,25 +1958,31 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1958 | 1958 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1959 | 1959 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1960 | 1960 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1961 | | const operand_ty = self.air.typeOfIndex(inst); |
| 1962 | 1961 | const ty = self.air.typeOf(bin_op.lhs); |
| 1963 | 1962 | |
| 1964 | | if (isByRef(operand_ty, self.target)) { |
| 1965 | | return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty.fmtDebug()}); |
| 1966 | | } |
| 1967 | | |
| 1968 | 1963 | return self.binOp(lhs, rhs, ty, op); |
| 1969 | 1964 | } |
| 1970 | 1965 | |
| 1971 | 1966 | fn 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 | } |
| 1974 | 1977 | |
| 1975 | 1978 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 1976 | 1979 | .op = op, |
| 1977 | 1980 | .valtype1 = typeToValtype(ty, self.target), |
| 1978 | 1981 | .signedness = if (ty.isSignedInt()) .signed else .unsigned, |
| 1979 | 1982 | }); |
| 1983 | try self.emitWValue(lhs); |
| 1984 | try self.emitWValue(rhs); |
| 1985 | |
| 1980 | 1986 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1981 | 1987 | |
| 1982 | 1988 | // save the result in a temporary |
| ... | ... | @@ -1985,6 +1991,37 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa |
| 1985 | 1991 | return bin_local; |
| 1986 | 1992 | } |
| 1987 | 1993 | |
| 1994 | fn 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 | |
| 1988 | 2025 | fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1989 | 2026 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1990 | 2027 | const lhs = try self.resolveInst(bin_op.lhs); |
| ... | ... | @@ -1993,10 +2030,6 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1993 | 2030 | const ty = self.air.typeOf(bin_op.lhs); |
| 1994 | 2031 | if (ty.zigTypeTag() == .Vector) { |
| 1995 | 2032 | 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", .{}); |
| 2000 | 2033 | } |
| 2001 | 2034 | |
| 2002 | 2035 | return self.wrapBinOp(lhs, rhs, ty, op); |
| ... | ... | @@ -2007,30 +2040,9 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError |
| 2007 | 2040 | var wasm_bits = toWasmBits(bit_size) orelse { |
| 2008 | 2041 | return self.fail("TODO: Implement wrapping arithmetic for integers with bitsize: {d}\n", .{bit_size}); |
| 2009 | 2042 | }; |
| 2043 | |
| 2010 | 2044 | 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); |
| 2034 | 2046 | } |
| 2035 | 2047 | |
| 2036 | 2048 | const opcode: wasm.Opcode = buildOpcode(.{ |
| ... | ... | @@ -2044,6 +2056,10 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError |
| 2044 | 2056 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2045 | 2057 | const bin_local = try self.allocLocal(ty); |
| 2046 | 2058 | try self.addLabel(.local_set, bin_local.local); |
| 2059 | if (wasm_bits == bit_size) { |
| 2060 | return bin_local; |
| 2061 | } |
| 2062 | |
| 2047 | 2063 | return self.wrapOperand(bin_local, ty); |
| 2048 | 2064 | } |
| 2049 | 2065 | |
| ... | ... | @@ -2994,7 +3010,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W |
| 2994 | 3010 | try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); |
| 2995 | 3011 | } |
| 2996 | 3012 | return stack_ptr; |
| 2997 | | } else return self.fail("todo Wasm @intCast to 128bit integers", .{}); |
| 3013 | } else return self.load(operand, wanted, 0); |
| 2998 | 3014 | |
| 2999 | 3015 | const result = try self.allocLocal(wanted); |
| 3000 | 3016 | 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 |
| 3760 | 3776 | const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or"); |
| 3761 | 3777 | |
| 3762 | 3778 | 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), |
| 3765 | 3781 | else => unreachable, |
| 3766 | 3782 | } |
| 3767 | 3783 | }, |
| 3768 | 3784 | else => { |
| 3769 | 3785 | 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); |
| 3771 | 3787 | const high_bit_cmp = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op); |
| 3772 | 3788 | const low_bit_cmp = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op); |
| 3773 | 3789 | |
| 3774 | 3790 | try self.emitWValue(low_bit_cmp); |
| 3775 | 3791 | try self.emitWValue(high_bit_cmp); |
| 3776 | | try self.emitWValue(low_bit_eql); |
| 3792 | try self.emitWValue(high_bit_eql); |
| 3777 | 3793 | try self.addTag(.select); |
| 3778 | 3794 | }, |
| 3779 | 3795 | } |