| ... | ... | @@ -952,10 +952,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 952 | 952 | .neg, |
| 953 | 953 | => try self.airUnaryMath(inst), |
| 954 | 954 | |
| 955 | | .add_with_overflow => try self.airAddSubShlWithOverflow(inst), |
| 956 | | .sub_with_overflow => try self.airAddSubShlWithOverflow(inst), |
| 955 | .add_with_overflow => try self.airAddSubWithOverflow(inst), |
| 956 | .sub_with_overflow => try self.airAddSubWithOverflow(inst), |
| 957 | 957 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 958 | | .shl_with_overflow => try self.airAddSubShlWithOverflow(inst), |
| 958 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 959 | 959 | |
| 960 | 960 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airMulDivBinOp(inst), |
| 961 | 961 | |
| ... | ... | @@ -1851,43 +1851,30 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1851 | 1851 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1852 | 1852 | } |
| 1853 | 1853 | |
| 1854 | | fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1854 | fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1855 | 1855 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1856 | 1856 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1857 | 1857 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1858 | 1858 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1859 | 1859 | const ty = self.air.typeOf(bin_op.lhs); |
| 1860 | 1860 | switch (ty.zigTypeTag()) { |
| 1861 | | .Vector => return self.fail("TODO implement add/sub/shl with overflow for Vector type", .{}), |
| 1861 | .Vector => return self.fail("TODO implement add/sub with overflow for Vector type", .{}), |
| 1862 | 1862 | .Int => { |
| 1863 | 1863 | try self.spillEflagsIfOccupied(); |
| 1864 | 1864 | |
| 1865 | | if (tag == .shl_with_overflow) { |
| 1866 | | try self.spillRegisters(&.{.rcx}); |
| 1867 | | // cf/of don't work for shifts other than 1 |
| 1868 | | return self.fail("TODO implement shl_with_overflow for x86_64", .{}); |
| 1869 | | } |
| 1870 | | |
| 1871 | | const partial: MCValue = switch (tag) { |
| 1865 | const partial_mcv = switch (tag) { |
| 1872 | 1866 | .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs), |
| 1873 | 1867 | .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs), |
| 1874 | | .shl_with_overflow => blk: { |
| 1875 | | try self.register_manager.getReg(.rcx, null); |
| 1876 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 1877 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 1878 | | const shift_ty = self.air.typeOf(bin_op.rhs); |
| 1879 | | break :blk try self.genShiftBinOp(.shl, null, lhs, rhs, ty, shift_ty); |
| 1880 | | }, |
| 1881 | 1868 | else => unreachable, |
| 1882 | 1869 | }; |
| 1883 | | |
| 1884 | 1870 | const int_info = ty.intInfo(self.target.*); |
| 1871 | const cc: Condition = switch (int_info.signedness) { |
| 1872 | .unsigned => .c, |
| 1873 | .signed => .o, |
| 1874 | }; |
| 1875 | |
| 1885 | 1876 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { |
| 1886 | | const cc: Condition = switch (int_info.signedness) { |
| 1887 | | .unsigned => .c, |
| 1888 | | .signed => .o, |
| 1889 | | }; |
| 1890 | | switch (partial) { |
| 1877 | switch (partial_mcv) { |
| 1891 | 1878 | .register => |reg| { |
| 1892 | 1879 | self.eflags_inst = inst; |
| 1893 | 1880 | break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; |
| ... | ... | @@ -1903,7 +1890,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1903 | 1890 | .{ .eflags = cc }, |
| 1904 | 1891 | .{}, |
| 1905 | 1892 | ); |
| 1906 | | try self.genSetStack(ty, dst_mcv.stack_offset, partial, .{}); |
| 1893 | try self.genSetStack(ty, dst_mcv.stack_offset, partial_mcv, .{}); |
| 1907 | 1894 | break :result dst_mcv; |
| 1908 | 1895 | } |
| 1909 | 1896 | |
| ... | ... | @@ -1913,7 +1900,78 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1913 | 1900 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 1914 | 1901 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); |
| 1915 | 1902 | |
| 1916 | | try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, partial.register); |
| 1903 | try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, partial_mcv.register, cc); |
| 1904 | |
| 1905 | break :result .{ .stack_offset = stack_offset }; |
| 1906 | }, |
| 1907 | else => unreachable, |
| 1908 | } |
| 1909 | }; |
| 1910 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1911 | } |
| 1912 | |
| 1913 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1914 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1915 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1916 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1917 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1918 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1919 | switch (lhs_ty.zigTypeTag()) { |
| 1920 | .Vector => return self.fail("TODO implement shl with overflow for Vector type", .{}), |
| 1921 | .Int => { |
| 1922 | try self.spillEflagsIfOccupied(); |
| 1923 | |
| 1924 | try self.register_manager.getReg(.rcx, null); |
| 1925 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1926 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1927 | |
| 1928 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1929 | |
| 1930 | const partial_mcv = try self.genShiftBinOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); |
| 1931 | const partial_lock = switch (partial_mcv) { |
| 1932 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 1933 | else => null, |
| 1934 | }; |
| 1935 | defer if (partial_lock) |lock| self.register_manager.unlockReg(lock); |
| 1936 | |
| 1937 | const tmp_mcv = try self.genShiftBinOp(.shr, null, partial_mcv, rhs, lhs_ty, rhs_ty); |
| 1938 | const tmp_lock = switch (tmp_mcv) { |
| 1939 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 1940 | else => null, |
| 1941 | }; |
| 1942 | defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock); |
| 1943 | |
| 1944 | try self.genBinOpMir(.cmp, lhs_ty, tmp_mcv, lhs); |
| 1945 | const cc = Condition.ne; |
| 1946 | |
| 1947 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { |
| 1948 | switch (partial_mcv) { |
| 1949 | .register => |reg| { |
| 1950 | self.eflags_inst = inst; |
| 1951 | break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; |
| 1952 | }, |
| 1953 | else => {}, |
| 1954 | } |
| 1955 | |
| 1956 | const abi_size = @intCast(i32, lhs_ty.abiSize(self.target.*)); |
| 1957 | const dst_mcv = try self.allocRegOrMem(inst, false); |
| 1958 | try self.genSetStack( |
| 1959 | Type.u1, |
| 1960 | dst_mcv.stack_offset - abi_size, |
| 1961 | .{ .eflags = cc }, |
| 1962 | .{}, |
| 1963 | ); |
| 1964 | try self.genSetStack(lhs_ty, dst_mcv.stack_offset, partial_mcv, .{}); |
| 1965 | break :result dst_mcv; |
| 1966 | } |
| 1967 | |
| 1968 | const tuple_ty = self.air.typeOfIndex(inst); |
| 1969 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| 1970 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| 1971 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 1972 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); |
| 1973 | |
| 1974 | try self.genSetStackTruncatedOverflowCompare(lhs_ty, stack_offset, overflow_bit_offset, partial_mcv.register, cc); |
| 1917 | 1975 | |
| 1918 | 1976 | break :result .{ .stack_offset = stack_offset }; |
| 1919 | 1977 | }, |
| ... | ... | @@ -1929,6 +1987,7 @@ fn genSetStackTruncatedOverflowCompare( |
| 1929 | 1987 | stack_offset: i32, |
| 1930 | 1988 | overflow_bit_offset: i32, |
| 1931 | 1989 | reg: Register, |
| 1990 | cc: Condition, |
| 1932 | 1991 | ) !void { |
| 1933 | 1992 | const reg_lock = self.register_manager.lockReg(reg); |
| 1934 | 1993 | defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -1946,10 +2005,6 @@ fn genSetStackTruncatedOverflowCompare( |
| 1946 | 2005 | }; |
| 1947 | 2006 | |
| 1948 | 2007 | const overflow_reg = temp_regs[0]; |
| 1949 | | const cc: Condition = switch (int_info.signedness) { |
| 1950 | | .signed => .o, |
| 1951 | | .unsigned => .c, |
| 1952 | | }; |
| 1953 | 2008 | try self.asmSetccRegister(overflow_reg.to8(), cc); |
| 1954 | 2009 | |
| 1955 | 2010 | const scratch_reg = temp_regs[1]; |
| ... | ... | @@ -1988,6 +2043,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1988 | 2043 | try self.spillEflagsIfOccupied(); |
| 1989 | 2044 | |
| 1990 | 2045 | const dst_info = dst_ty.intInfo(self.target.*); |
| 2046 | const cc: Condition = switch (dst_info.signedness) { |
| 2047 | .unsigned => .c, |
| 2048 | .signed => .o, |
| 2049 | }; |
| 1991 | 2050 | if (dst_info.bits >= 8 and math.isPowerOfTwo(dst_info.bits)) { |
| 1992 | 2051 | var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) { |
| 1993 | 2052 | .signed => .int_signed, |
| ... | ... | @@ -2003,12 +2062,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2003 | 2062 | const lhs = try self.resolveInst(bin_op.lhs); |
| 2004 | 2063 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2005 | 2064 | |
| 2006 | | const partial = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs); |
| 2007 | | const cc: Condition = switch (dst_info.signedness) { |
| 2008 | | .unsigned => .c, |
| 2009 | | .signed => .o, |
| 2010 | | }; |
| 2011 | | switch (partial) { |
| 2065 | const partial_mcv = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs); |
| 2066 | switch (partial_mcv) { |
| 2012 | 2067 | .register => |reg| { |
| 2013 | 2068 | self.eflags_inst = inst; |
| 2014 | 2069 | break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; |
| ... | ... | @@ -2024,7 +2079,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2024 | 2079 | .{ .eflags = cc }, |
| 2025 | 2080 | .{}, |
| 2026 | 2081 | ); |
| 2027 | | try self.genSetStack(dst_ty, dst_mcv.stack_offset, partial, .{}); |
| 2082 | try self.genSetStack(dst_ty, dst_mcv.stack_offset, partial_mcv, .{}); |
| 2028 | 2083 | break :result dst_mcv; |
| 2029 | 2084 | } |
| 2030 | 2085 | |
| ... | ... | @@ -2079,7 +2134,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2079 | 2134 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 2080 | 2135 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); |
| 2081 | 2136 | |
| 2082 | | try self.genSetStackTruncatedOverflowCompare(dst_ty, stack_offset, overflow_bit_offset, dst_reg); |
| 2137 | try self.genSetStackTruncatedOverflowCompare(dst_ty, stack_offset, overflow_bit_offset, dst_reg, cc); |
| 2083 | 2138 | |
| 2084 | 2139 | break :result .{ .stack_offset = stack_offset }; |
| 2085 | 2140 | }, |