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