| ... | ... | @@ -316,7 +316,7 @@ pub fn generate( |
| 316 | 316 | defer function.mir_extra.deinit(bin_file.allocator); |
| 317 | 317 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); |
| 318 | 318 | |
| 319 | | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 319 | var call_info = function.resolveCallingConventionValues(fn_type, &.{}) catch |err| switch (err) { |
| 320 | 320 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 321 | 321 | error.OutOfRegisters => return Result{ |
| 322 | 322 | .fail = try ErrorMsg.create( |
| ... | ... | @@ -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 | |
| ... | ... | @@ -1625,24 +1625,68 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void |
| 1625 | 1625 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1626 | 1626 | } |
| 1627 | 1627 | |
| 1628 | fn activeIntBits(self: *Self, dst_air: Air.Inst.Ref) u16 { |
| 1629 | const air_tag = self.air.instructions.items(.tag); |
| 1630 | const air_data = self.air.instructions.items(.data); |
| 1631 | |
| 1632 | const dst_ty = self.air.typeOf(dst_air); |
| 1633 | const dst_info = dst_ty.intInfo(self.target.*); |
| 1634 | if (Air.refToIndex(dst_air)) |inst| { |
| 1635 | switch (air_tag[inst]) { |
| 1636 | .constant => { |
| 1637 | const src_val = self.air.values[air_data[inst].ty_pl.payload]; |
| 1638 | var space: Value.BigIntSpace = undefined; |
| 1639 | const src_int = src_val.toBigInt(&space, self.target.*); |
| 1640 | return @intCast(u16, src_int.bitCountTwosComp()) + |
| 1641 | @boolToInt(src_int.positive and dst_info.signedness == .signed); |
| 1642 | }, |
| 1643 | .intcast => { |
| 1644 | const src_ty = self.air.typeOf(air_data[inst].ty_op.operand); |
| 1645 | const src_info = src_ty.intInfo(self.target.*); |
| 1646 | return @min(switch (src_info.signedness) { |
| 1647 | .signed => switch (dst_info.signedness) { |
| 1648 | .signed => src_info.bits, |
| 1649 | .unsigned => src_info.bits - 1, |
| 1650 | }, |
| 1651 | .unsigned => switch (dst_info.signedness) { |
| 1652 | .signed => src_info.bits + 1, |
| 1653 | .unsigned => src_info.bits, |
| 1654 | }, |
| 1655 | }, dst_info.bits); |
| 1656 | }, |
| 1657 | else => {}, |
| 1658 | } |
| 1659 | } |
| 1660 | return dst_info.bits; |
| 1661 | } |
| 1662 | |
| 1628 | 1663 | fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1629 | 1664 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1630 | | const result = result: { |
| 1631 | | if (self.liveness.isUnused(inst)) break :result .dead; |
| 1632 | | |
| 1665 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1633 | 1666 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1634 | | const ty = self.air.typeOfIndex(inst); |
| 1635 | | |
| 1636 | | if (ty.zigTypeTag() == .Float) { |
| 1667 | const dst_ty = self.air.typeOfIndex(inst); |
| 1668 | if (dst_ty.zigTypeTag() == .Float) |
| 1637 | 1669 | break :result try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); |
| 1638 | | } |
| 1639 | 1670 | |
| 1640 | | try self.spillRegisters(&.{ .rax, .rdx }); |
| 1671 | const dst_info = dst_ty.intInfo(self.target.*); |
| 1672 | var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) { |
| 1673 | .signed => .int_signed, |
| 1674 | .unsigned => .int_unsigned, |
| 1675 | } }, .data = switch (tag) { |
| 1676 | else => unreachable, |
| 1677 | .mul, .mulwrap => std.math.max3( |
| 1678 | self.activeIntBits(bin_op.lhs), |
| 1679 | self.activeIntBits(bin_op.rhs), |
| 1680 | dst_info.bits / 2, |
| 1681 | ), |
| 1682 | .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_info.bits, |
| 1683 | } }; |
| 1684 | const src_ty = Type.initPayload(&src_pl.base); |
| 1641 | 1685 | |
| 1686 | try self.spillRegisters(&.{ .rax, .rdx }); |
| 1642 | 1687 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1643 | 1688 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1644 | | |
| 1645 | | break :result try self.genMulDivBinOp(tag, inst, ty, lhs, rhs); |
| 1689 | break :result try self.genMulDivBinOp(tag, inst, dst_ty, src_ty, lhs, rhs); |
| 1646 | 1690 | }; |
| 1647 | 1691 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1648 | 1692 | } |
| ... | ... | @@ -1795,7 +1839,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1795 | 1839 | break :cc .c; |
| 1796 | 1840 | }; |
| 1797 | 1841 | |
| 1798 | | const dst_mcv = try self.genMulDivBinOp(.mul, inst, ty, lhs_mcv, rhs_mcv); |
| 1842 | const dst_mcv = try self.genMulDivBinOp(.mul, inst, ty, ty, lhs_mcv, rhs_mcv); |
| 1799 | 1843 | const abi_size = @intCast(u32, @max(ty.abiSize(self.target.*), 2)); |
| 1800 | 1844 | try self.asmCmovccRegisterRegister( |
| 1801 | 1845 | registerAlias(dst_mcv.register, abi_size), |
| ... | ... | @@ -1807,70 +1851,133 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1807 | 1851 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1808 | 1852 | } |
| 1809 | 1853 | |
| 1810 | | fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1854 | fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1811 | 1855 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1812 | | const tag = self.air.instructions.items(.tag)[inst]; |
| 1813 | 1856 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1814 | | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1857 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1858 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1815 | 1859 | const ty = self.air.typeOf(bin_op.lhs); |
| 1816 | | const abi_size = ty.abiSize(self.target.*); |
| 1817 | 1860 | switch (ty.zigTypeTag()) { |
| 1818 | | .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", .{}), |
| 1819 | 1862 | .Int => { |
| 1820 | | if (abi_size > 8) { |
| 1821 | | return self.fail("TODO implement add/sub/shl with overflow for Ints larger than 64bits", .{}); |
| 1822 | | } |
| 1823 | | |
| 1824 | 1863 | try self.spillEflagsIfOccupied(); |
| 1825 | 1864 | |
| 1826 | | if (tag == .shl_with_overflow) { |
| 1827 | | try self.spillRegisters(&.{.rcx}); |
| 1828 | | } |
| 1829 | | |
| 1830 | | const partial: MCValue = switch (tag) { |
| 1865 | const partial_mcv = switch (tag) { |
| 1831 | 1866 | .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs), |
| 1832 | 1867 | .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs), |
| 1833 | | .shl_with_overflow => blk: { |
| 1834 | | try self.register_manager.getReg(.rcx, null); |
| 1835 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 1836 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 1837 | | const shift_ty = self.air.typeOf(bin_op.rhs); |
| 1838 | | break :blk try self.genShiftBinOp(.shl, null, lhs, rhs, ty, shift_ty); |
| 1839 | | }, |
| 1840 | 1868 | else => unreachable, |
| 1841 | 1869 | }; |
| 1842 | | |
| 1843 | 1870 | const int_info = ty.intInfo(self.target.*); |
| 1871 | const cc: Condition = switch (int_info.signedness) { |
| 1872 | .unsigned => .c, |
| 1873 | .signed => .o, |
| 1874 | }; |
| 1844 | 1875 | |
| 1845 | | if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { |
| 1846 | | self.eflags_inst = inst; |
| 1876 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { |
| 1877 | switch (partial_mcv) { |
| 1878 | .register => |reg| { |
| 1879 | self.eflags_inst = inst; |
| 1880 | break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; |
| 1881 | }, |
| 1882 | else => {}, |
| 1883 | } |
| 1847 | 1884 | |
| 1848 | | const cc: Condition = switch (int_info.signedness) { |
| 1849 | | .unsigned => .c, |
| 1850 | | .signed => .o, |
| 1851 | | }; |
| 1852 | | break :result MCValue{ .register_overflow = .{ |
| 1853 | | .reg = partial.register, |
| 1854 | | .eflags = cc, |
| 1855 | | } }; |
| 1885 | const abi_size = @intCast(i32, ty.abiSize(self.target.*)); |
| 1886 | const dst_mcv = try self.allocRegOrMem(inst, false); |
| 1887 | try self.genSetStack( |
| 1888 | Type.u1, |
| 1889 | dst_mcv.stack_offset - abi_size, |
| 1890 | .{ .eflags = cc }, |
| 1891 | .{}, |
| 1892 | ); |
| 1893 | try self.genSetStack(ty, dst_mcv.stack_offset, partial_mcv, .{}); |
| 1894 | break :result dst_mcv; |
| 1856 | 1895 | } |
| 1857 | 1896 | |
| 1858 | | self.eflags_inst = null; |
| 1859 | | |
| 1860 | 1897 | const tuple_ty = self.air.typeOfIndex(inst); |
| 1861 | 1898 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| 1862 | 1899 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| 1863 | 1900 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 1864 | 1901 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); |
| 1865 | 1902 | |
| 1866 | | 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); |
| 1867 | 1904 | |
| 1868 | | break :result MCValue{ .stack_offset = stack_offset }; |
| 1905 | break :result .{ .stack_offset = stack_offset }; |
| 1869 | 1906 | }, |
| 1870 | 1907 | else => unreachable, |
| 1871 | 1908 | } |
| 1872 | 1909 | }; |
| 1910 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1911 | } |
| 1873 | 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); |
| 1975 | |
| 1976 | break :result .{ .stack_offset = stack_offset }; |
| 1977 | }, |
| 1978 | else => unreachable, |
| 1979 | } |
| 1980 | }; |
| 1874 | 1981 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1875 | 1982 | } |
| 1876 | 1983 | |
| ... | ... | @@ -1880,6 +1987,7 @@ fn genSetStackTruncatedOverflowCompare( |
| 1880 | 1987 | stack_offset: i32, |
| 1881 | 1988 | overflow_bit_offset: i32, |
| 1882 | 1989 | reg: Register, |
| 1990 | cc: Condition, |
| 1883 | 1991 | ) !void { |
| 1884 | 1992 | const reg_lock = self.register_manager.lockReg(reg); |
| 1885 | 1993 | defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -1897,10 +2005,6 @@ fn genSetStackTruncatedOverflowCompare( |
| 1897 | 2005 | }; |
| 1898 | 2006 | |
| 1899 | 2007 | const overflow_reg = temp_regs[0]; |
| 1900 | | const cc: Condition = switch (int_info.signedness) { |
| 1901 | | .signed => .o, |
| 1902 | | .unsigned => .c, |
| 1903 | | }; |
| 1904 | 2008 | try self.asmSetccRegister(overflow_reg.to8(), cc); |
| 1905 | 2009 | |
| 1906 | 2010 | const scratch_reg = temp_regs[1]; |
| ... | ... | @@ -1923,7 +2027,7 @@ fn genSetStackTruncatedOverflowCompare( |
| 1923 | 2027 | ); |
| 1924 | 2028 | |
| 1925 | 2029 | try self.genSetStack(ty, stack_offset, .{ .register = scratch_reg }, .{}); |
| 1926 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| 2030 | try self.genSetStack(Type.u1, stack_offset - overflow_bit_offset, .{ |
| 1927 | 2031 | .register = overflow_reg.to8(), |
| 1928 | 2032 | }, .{}); |
| 1929 | 2033 | } |
| ... | ... | @@ -1931,48 +2035,56 @@ fn genSetStackTruncatedOverflowCompare( |
| 1931 | 2035 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1932 | 2036 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1933 | 2037 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1934 | | |
| 1935 | | if (self.liveness.isUnused(inst)) { |
| 1936 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1937 | | } |
| 1938 | | |
| 1939 | | const ty = self.air.typeOf(bin_op.lhs); |
| 1940 | | const abi_size = ty.abiSize(self.target.*); |
| 1941 | | const result: MCValue = result: { |
| 1942 | | switch (ty.zigTypeTag()) { |
| 2038 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2039 | const dst_ty = self.air.typeOf(bin_op.lhs); |
| 2040 | switch (dst_ty.zigTypeTag()) { |
| 1943 | 2041 | .Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}), |
| 1944 | 2042 | .Int => { |
| 1945 | | if (abi_size > 8) { |
| 1946 | | return self.fail("TODO implement mul_with_overflow for Ints larger than 64bits", .{}); |
| 1947 | | } |
| 1948 | | |
| 1949 | | const int_info = ty.intInfo(self.target.*); |
| 2043 | try self.spillEflagsIfOccupied(); |
| 1950 | 2044 | |
| 1951 | | if (math.isPowerOfTwo(int_info.bits) and int_info.bits >= 8) { |
| 1952 | | try self.spillEflagsIfOccupied(); |
| 1953 | | self.eflags_inst = inst; |
| 2045 | const dst_info = dst_ty.intInfo(self.target.*); |
| 2046 | const cc: Condition = switch (dst_info.signedness) { |
| 2047 | .unsigned => .c, |
| 2048 | .signed => .o, |
| 2049 | }; |
| 2050 | if (dst_info.bits >= 8 and math.isPowerOfTwo(dst_info.bits)) { |
| 2051 | var src_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dst_info.signedness) { |
| 2052 | .signed => .int_signed, |
| 2053 | .unsigned => .int_unsigned, |
| 2054 | } }, .data = std.math.max3( |
| 2055 | self.activeIntBits(bin_op.lhs), |
| 2056 | self.activeIntBits(bin_op.rhs), |
| 2057 | dst_info.bits / 2, |
| 2058 | ) }; |
| 2059 | const src_ty = Type.initPayload(&src_pl.base); |
| 1954 | 2060 | |
| 1955 | 2061 | try self.spillRegisters(&.{ .rax, .rdx }); |
| 1956 | | |
| 1957 | 2062 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1958 | 2063 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1959 | 2064 | |
| 1960 | | const partial = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); |
| 1961 | | const cc: Condition = switch (int_info.signedness) { |
| 1962 | | .unsigned => .c, |
| 1963 | | .signed => .o, |
| 1964 | | }; |
| 1965 | | break :result MCValue{ .register_overflow = .{ |
| 1966 | | .reg = partial.register, |
| 1967 | | .eflags = cc, |
| 1968 | | } }; |
| 1969 | | } |
| 2065 | const partial_mcv = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs); |
| 2066 | switch (partial_mcv) { |
| 2067 | .register => |reg| { |
| 2068 | self.eflags_inst = inst; |
| 2069 | break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; |
| 2070 | }, |
| 2071 | else => {}, |
| 2072 | } |
| 1970 | 2073 | |
| 1971 | | try self.spillEflagsIfOccupied(); |
| 1972 | | self.eflags_inst = null; |
| 2074 | const dst_abi_size = @intCast(i32, dst_ty.abiSize(self.target.*)); |
| 2075 | const dst_mcv = try self.allocRegOrMem(inst, false); |
| 2076 | try self.genSetStack( |
| 2077 | Type.u1, |
| 2078 | dst_mcv.stack_offset - dst_abi_size, |
| 2079 | .{ .eflags = cc }, |
| 2080 | .{}, |
| 2081 | ); |
| 2082 | try self.genSetStack(dst_ty, dst_mcv.stack_offset, partial_mcv, .{}); |
| 2083 | break :result dst_mcv; |
| 2084 | } |
| 1973 | 2085 | |
| 1974 | 2086 | const dst_reg: Register = dst_reg: { |
| 1975 | | switch (int_info.signedness) { |
| 2087 | switch (dst_info.signedness) { |
| 1976 | 2088 | .signed => { |
| 1977 | 2089 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1978 | 2090 | const rhs = try self.resolveInst(bin_op.rhs); |
| ... | ... | @@ -1985,14 +2097,14 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1985 | 2097 | |
| 1986 | 2098 | const dst_reg: Register = blk: { |
| 1987 | 2099 | if (lhs.isRegister()) break :blk lhs.register; |
| 1988 | | break :blk try self.copyToTmpRegister(ty, lhs); |
| 2100 | break :blk try self.copyToTmpRegister(dst_ty, lhs); |
| 1989 | 2101 | }; |
| 1990 | 2102 | const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); |
| 1991 | 2103 | defer self.register_manager.unlockReg(dst_reg_lock); |
| 1992 | 2104 | |
| 1993 | 2105 | const rhs_mcv: MCValue = blk: { |
| 1994 | 2106 | if (rhs.isRegister() or rhs.isMemory()) break :blk rhs; |
| 1995 | | break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) }; |
| 2107 | break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) }; |
| 1996 | 2108 | }; |
| 1997 | 2109 | const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) { |
| 1998 | 2110 | .register => |reg| self.register_manager.lockReg(reg), |
| ... | ... | @@ -2010,7 +2122,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2010 | 2122 | const lhs = try self.resolveInst(bin_op.lhs); |
| 2011 | 2123 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2012 | 2124 | |
| 2013 | | const dst_mcv = try self.genMulDivBinOp(.mul, null, ty, lhs, rhs); |
| 2125 | const dst_mcv = try self.genMulDivBinOp(.mul, null, dst_ty, dst_ty, lhs, rhs); |
| 2014 | 2126 | break :dst_reg dst_mcv.register; |
| 2015 | 2127 | }, |
| 2016 | 2128 | } |
| ... | ... | @@ -2022,14 +2134,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2022 | 2134 | const overflow_bit_offset = @intCast(i32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 2023 | 2135 | const stack_offset = @intCast(i32, try self.allocMem(inst, tuple_size, tuple_align)); |
| 2024 | 2136 | |
| 2025 | | try self.genSetStackTruncatedOverflowCompare(ty, stack_offset, overflow_bit_offset, dst_reg); |
| 2137 | try self.genSetStackTruncatedOverflowCompare(dst_ty, stack_offset, overflow_bit_offset, dst_reg, cc); |
| 2026 | 2138 | |
| 2027 | | break :result MCValue{ .stack_offset = stack_offset }; |
| 2139 | break :result .{ .stack_offset = stack_offset }; |
| 2028 | 2140 | }, |
| 2029 | 2141 | else => unreachable, |
| 2030 | 2142 | } |
| 2031 | 2143 | }; |
| 2032 | | |
| 2033 | 2144 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2034 | 2145 | } |
| 2035 | 2146 | |
| ... | ... | @@ -2040,7 +2151,6 @@ fn genIntMulDivOpMir( |
| 2040 | 2151 | self: *Self, |
| 2041 | 2152 | tag: Mir.Inst.Tag, |
| 2042 | 2153 | ty: Type, |
| 2043 | | signedness: std.builtin.Signedness, |
| 2044 | 2154 | lhs: MCValue, |
| 2045 | 2155 | rhs: MCValue, |
| 2046 | 2156 | ) !void { |
| ... | ... | @@ -2057,26 +2167,23 @@ fn genIntMulDivOpMir( |
| 2057 | 2167 | try self.genSetReg(ty, .rax, lhs); |
| 2058 | 2168 | } |
| 2059 | 2169 | |
| 2060 | | switch (signedness) { |
| 2061 | | .signed => try self.asmOpOnly(.cqo), |
| 2062 | | .unsigned => try self.asmRegisterRegister(.xor, .rdx, .rdx), |
| 2170 | switch (tag) { |
| 2171 | else => unreachable, |
| 2172 | .mul, .imul => {}, |
| 2173 | .div => try self.asmRegisterRegister(.xor, .edx, .edx), |
| 2174 | .idiv => try self.asmOpOnly(.cqo), |
| 2063 | 2175 | } |
| 2064 | 2176 | |
| 2065 | | const factor = switch (rhs) { |
| 2066 | | .register => rhs, |
| 2067 | | .stack_offset => rhs, |
| 2068 | | else => blk: { |
| 2069 | | const reg = try self.copyToTmpRegister(ty, rhs); |
| 2070 | | break :blk MCValue{ .register = reg }; |
| 2071 | | }, |
| 2177 | const factor: MCValue = switch (rhs) { |
| 2178 | .register, .stack_offset => rhs, |
| 2179 | else => .{ .register = try self.copyToTmpRegister(ty, rhs) }, |
| 2072 | 2180 | }; |
| 2073 | | |
| 2074 | 2181 | switch (factor) { |
| 2075 | 2182 | .register => |reg| try self.asmRegister(tag, reg), |
| 2076 | | .stack_offset => |off| try self.asmMemory(tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 2077 | | .base = .rbp, |
| 2078 | | .disp = -off, |
| 2079 | | })), |
| 2183 | .stack_offset => |off| try self.asmMemory(tag, Memory.sib( |
| 2184 | Memory.PtrSize.fromSize(abi_size), |
| 2185 | .{ .base = .rbp, .disp = -off }, |
| 2186 | )), |
| 2080 | 2187 | else => unreachable, |
| 2081 | 2188 | } |
| 2082 | 2189 | } |
| ... | ... | @@ -2102,7 +2209,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa |
| 2102 | 2209 | try self.genIntMulDivOpMir(switch (signedness) { |
| 2103 | 2210 | .signed => .idiv, |
| 2104 | 2211 | .unsigned => .div, |
| 2105 | | }, Type.isize, signedness, .{ .register = dividend }, .{ .register = divisor }); |
| 2212 | }, Type.isize, .{ .register = dividend }, .{ .register = divisor }); |
| 2106 | 2213 | |
| 2107 | 2214 | try self.asmRegisterRegister(.xor, divisor.to64(), dividend.to64()); |
| 2108 | 2215 | try self.asmRegisterImmediate(.sar, divisor.to64(), Immediate.u(63)); |
| ... | ... | @@ -3818,45 +3925,95 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32 |
| 3818 | 3925 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3819 | 3926 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3820 | 3927 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 3821 | | const operand = extra.struct_operand; |
| 3822 | | const index = extra.field_index; |
| 3928 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3929 | const operand = extra.struct_operand; |
| 3930 | const index = extra.field_index; |
| 3931 | |
| 3932 | const container_ty = self.air.typeOf(operand); |
| 3933 | const field_ty = container_ty.structFieldType(index); |
| 3934 | if (!field_ty.hasRuntimeBitsIgnoreComptime()) break :result .none; |
| 3935 | |
| 3936 | const src_mcv = try self.resolveInst(operand); |
| 3937 | const field_off = switch (container_ty.containerLayout()) { |
| 3938 | .Auto, .Extern => @intCast(u32, container_ty.structFieldOffset(index, self.target.*) * 8), |
| 3939 | .Packed => if (container_ty.castTag(.@"struct")) |struct_obj| |
| 3940 | struct_obj.data.packedFieldBitOffset(self.target.*, index) |
| 3941 | else |
| 3942 | 0, |
| 3943 | }; |
| 3823 | 3944 | |
| 3824 | | if (self.liveness.isUnused(inst)) { |
| 3825 | | return self.finishAir(inst, .dead, .{ extra.struct_operand, .none, .none }); |
| 3826 | | } |
| 3945 | switch (src_mcv) { |
| 3946 | .stack_offset => |src_off| { |
| 3947 | const field_abi_size = @intCast(u32, field_ty.abiSize(self.target.*)); |
| 3948 | const limb_abi_size = @min(field_abi_size, 8); |
| 3949 | const limb_abi_bits = limb_abi_size * 8; |
| 3950 | const field_byte_off = @intCast(i32, field_off / limb_abi_bits * limb_abi_size); |
| 3951 | const field_bit_off = field_off % limb_abi_bits; |
| 3952 | |
| 3953 | if (field_bit_off == 0) { |
| 3954 | const off_mcv = MCValue{ .stack_offset = src_off - field_byte_off }; |
| 3955 | if (self.reuseOperand(inst, operand, 0, src_mcv)) break :result off_mcv; |
| 3956 | |
| 3957 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 3958 | try self.setRegOrMem(field_ty, dst_mcv, off_mcv); |
| 3959 | break :result dst_mcv; |
| 3960 | } |
| 3827 | 3961 | |
| 3828 | | const mcv = try self.resolveInst(operand); |
| 3829 | | const container_ty = self.air.typeOf(operand); |
| 3830 | | const field_ty = container_ty.structFieldType(index); |
| 3831 | | const field_bit_offset = switch (container_ty.containerLayout()) { |
| 3832 | | .Auto, .Extern => @intCast(u32, container_ty.structFieldOffset(index, self.target.*) * 8), |
| 3833 | | .Packed => if (container_ty.castTag(.@"struct")) |struct_obj| |
| 3834 | | struct_obj.data.packedFieldBitOffset(self.target.*, index) |
| 3835 | | else |
| 3836 | | 0, |
| 3837 | | }; |
| 3962 | if (field_abi_size > 8) { |
| 3963 | return self.fail("TODO implement struct_field_val with large packed field", .{}); |
| 3964 | } |
| 3838 | 3965 | |
| 3839 | | const result: MCValue = result: { |
| 3840 | | switch (mcv) { |
| 3841 | | .stack_offset => |off| { |
| 3842 | | const byte_offset = std.math.divExact(u32, field_bit_offset, 8) catch |
| 3843 | | return self.fail("TODO implement struct_field_val for a packed struct", .{}); |
| 3844 | | break :result MCValue{ .stack_offset = off - @intCast(i32, byte_offset) }; |
| 3966 | const dst_reg = try self.register_manager.allocReg(inst, gp); |
| 3967 | const field_extra_bits = self.regExtraBits(field_ty); |
| 3968 | const load_abi_size = |
| 3969 | if (field_bit_off < field_extra_bits) field_abi_size else field_abi_size * 2; |
| 3970 | if (load_abi_size <= 8) { |
| 3971 | const load_reg = registerAlias(dst_reg, load_abi_size); |
| 3972 | try self.asmRegisterMemory(.mov, load_reg, Memory.sib( |
| 3973 | Memory.PtrSize.fromSize(load_abi_size), |
| 3974 | .{ .base = .rbp, .disp = field_byte_off - src_off }, |
| 3975 | )); |
| 3976 | try self.asmRegisterImmediate(.shr, load_reg, Immediate.u(field_bit_off)); |
| 3977 | } else { |
| 3978 | const tmp_reg = registerAlias( |
| 3979 | try self.register_manager.allocReg(null, gp), |
| 3980 | field_abi_size, |
| 3981 | ); |
| 3982 | const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 3983 | defer self.register_manager.unlockReg(tmp_lock); |
| 3984 | |
| 3985 | const dst_alias = registerAlias(dst_reg, field_abi_size); |
| 3986 | try self.asmRegisterMemory(.mov, dst_alias, Memory.sib( |
| 3987 | Memory.PtrSize.fromSize(field_abi_size), |
| 3988 | .{ .base = .rbp, .disp = field_byte_off - src_off }, |
| 3989 | )); |
| 3990 | try self.asmRegisterMemory(.mov, tmp_reg, Memory.sib( |
| 3991 | Memory.PtrSize.fromSize(field_abi_size), |
| 3992 | .{ .base = .rbp, .disp = field_byte_off + 1 - src_off }, |
| 3993 | )); |
| 3994 | try self.asmRegisterRegisterImmediate( |
| 3995 | .shrd, |
| 3996 | dst_alias, |
| 3997 | tmp_reg, |
| 3998 | Immediate.u(field_bit_off), |
| 3999 | ); |
| 4000 | } |
| 4001 | |
| 4002 | if (field_extra_bits > 0) try self.truncateRegister(field_ty, dst_reg); |
| 4003 | break :result .{ .register = dst_reg }; |
| 3845 | 4004 | }, |
| 3846 | 4005 | .register => |reg| { |
| 3847 | 4006 | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); |
| 3848 | 4007 | defer self.register_manager.unlockReg(reg_lock); |
| 3849 | 4008 | |
| 3850 | | const dst_mcv: MCValue = blk: { |
| 3851 | | if (self.reuseOperand(inst, operand, 0, mcv)) { |
| 3852 | | break :blk mcv; |
| 3853 | | } else { |
| 3854 | | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, Type.usize, .{ |
| 3855 | | .register = reg.to64(), |
| 3856 | | }); |
| 3857 | | break :blk dst_mcv; |
| 3858 | | } |
| 3859 | | }; |
| 4009 | const dst_mcv = if (self.reuseOperand(inst, operand, 0, src_mcv)) |
| 4010 | src_mcv |
| 4011 | else |
| 4012 | try self.copyToRegisterWithInstTracking( |
| 4013 | inst, |
| 4014 | Type.usize, |
| 4015 | .{ .register = reg.to64() }, |
| 4016 | ); |
| 3860 | 4017 | const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { |
| 3861 | 4018 | .register => |a_reg| self.register_manager.lockReg(a_reg), |
| 3862 | 4019 | else => null, |
| ... | ... | @@ -3864,7 +4021,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3864 | 4021 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); |
| 3865 | 4022 | |
| 3866 | 4023 | // Shift by struct_field_offset. |
| 3867 | | try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv, .{ .immediate = field_bit_offset }); |
| 4024 | try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv, .{ .immediate = field_off }); |
| 3868 | 4025 | |
| 3869 | 4026 | // Mask to field_bit_size bits |
| 3870 | 4027 | const field_bit_size = field_ty.bitSize(self.target.*); |
| ... | ... | @@ -3883,31 +4040,34 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3883 | 4040 | registerAlias(dst_mcv.register, field_byte_size), |
| 3884 | 4041 | ); |
| 3885 | 4042 | } |
| 3886 | | |
| 3887 | 4043 | break :result dst_mcv; |
| 3888 | 4044 | }, |
| 3889 | 4045 | .register_overflow => |ro| { |
| 3890 | 4046 | switch (index) { |
| 3891 | | 0 => { |
| 3892 | | // Get wrapped value for overflow operation. |
| 3893 | | break :result MCValue{ .register = ro.reg }; |
| 3894 | | }, |
| 3895 | | 1 => { |
| 3896 | | // Get overflow bit. |
| 3897 | | const reg_lock = self.register_manager.lockRegAssumeUnused(ro.reg); |
| 3898 | | defer self.register_manager.unlockReg(reg_lock); |
| 3899 | | |
| 4047 | // Get wrapped value for overflow operation. |
| 4048 | 0 => break :result if (self.liveness.operandDies(inst, 0)) |
| 4049 | .{ .register = ro.reg } |
| 4050 | else |
| 4051 | try self.copyToRegisterWithInstTracking( |
| 4052 | inst, |
| 4053 | Type.usize, |
| 4054 | .{ .register = ro.reg }, |
| 4055 | ), |
| 4056 | // Get overflow bit. |
| 4057 | 1 => if (self.liveness.operandDies(inst, 0)) { |
| 4058 | self.eflags_inst = inst; |
| 4059 | break :result .{ .eflags = ro.eflags }; |
| 4060 | } else { |
| 3900 | 4061 | const dst_reg = try self.register_manager.allocReg(inst, gp); |
| 3901 | 4062 | try self.asmSetccRegister(dst_reg.to8(), ro.eflags); |
| 3902 | | break :result MCValue{ .register = dst_reg.to8() }; |
| 4063 | break :result .{ .register = dst_reg.to8() }; |
| 3903 | 4064 | }, |
| 3904 | 4065 | else => unreachable, |
| 3905 | 4066 | } |
| 3906 | 4067 | }, |
| 3907 | | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), |
| 4068 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{src_mcv}), |
| 3908 | 4069 | } |
| 3909 | 4070 | }; |
| 3910 | | |
| 3911 | 4071 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 3912 | 4072 | } |
| 3913 | 4073 | |
| ... | ... | @@ -3986,7 +4146,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue |
| 3986 | 4146 | .eflags => unreachable, |
| 3987 | 4147 | .register_overflow => unreachable, |
| 3988 | 4148 | .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), |
| 3989 | | .ptr_stack_offset, .stack_offset => |off| { |
| 4149 | .stack_offset => |off| { |
| 3990 | 4150 | if (abi_size > 8) { |
| 3991 | 4151 | return self.fail("TODO implement {} for stack dst with large ABI", .{mir_tag}); |
| 3992 | 4152 | } |
| ... | ... | @@ -3996,20 +4156,13 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue |
| 3996 | 4156 | .disp = -off, |
| 3997 | 4157 | })); |
| 3998 | 4158 | }, |
| 4159 | .ptr_stack_offset => unreachable, |
| 3999 | 4160 | .memory, .linker_load => { |
| 4000 | 4161 | const addr_reg = (try self.register_manager.allocReg(null, gp)).to64(); |
| 4001 | 4162 | const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); |
| 4002 | 4163 | defer self.register_manager.unlockReg(addr_reg_lock); |
| 4003 | 4164 | |
| 4004 | 4165 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_mcv); |
| 4005 | | |
| 4006 | | // To get the actual address of the value we want to modify we have to go through the GOT |
| 4007 | | try self.asmRegisterMemory( |
| 4008 | | .mov, |
| 4009 | | addr_reg, |
| 4010 | | Memory.sib(.qword, .{ .base = addr_reg }), |
| 4011 | | ); |
| 4012 | | |
| 4013 | 4166 | try self.asmMemory( |
| 4014 | 4167 | mir_tag, |
| 4015 | 4168 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = addr_reg }), |
| ... | ... | @@ -4272,20 +4425,26 @@ fn genMulDivBinOp( |
| 4272 | 4425 | self: *Self, |
| 4273 | 4426 | tag: Air.Inst.Tag, |
| 4274 | 4427 | maybe_inst: ?Air.Inst.Index, |
| 4275 | | ty: Type, |
| 4428 | dst_ty: Type, |
| 4429 | src_ty: Type, |
| 4276 | 4430 | lhs: MCValue, |
| 4277 | 4431 | rhs: MCValue, |
| 4278 | 4432 | ) !MCValue { |
| 4279 | | if (ty.zigTypeTag() == .Vector or ty.zigTypeTag() == .Float) { |
| 4280 | | return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()}); |
| 4281 | | } |
| 4282 | | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 4283 | | if (abi_size > 8) { |
| 4284 | | return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()}); |
| 4285 | | } |
| 4286 | | if (tag == .div_float) { |
| 4287 | | return self.fail("TODO implement genMulDivBinOp for div_float", .{}); |
| 4433 | if (dst_ty.zigTypeTag() == .Vector or dst_ty.zigTypeTag() == .Float) { |
| 4434 | return self.fail("TODO implement genMulDivBinOp for {}", .{dst_ty.fmtDebug()}); |
| 4288 | 4435 | } |
| 4436 | const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); |
| 4437 | const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*)); |
| 4438 | if (switch (tag) { |
| 4439 | else => unreachable, |
| 4440 | .mul, .mulwrap => dst_abi_size != src_abi_size and dst_abi_size != src_abi_size * 2, |
| 4441 | .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_abi_size != src_abi_size, |
| 4442 | } or src_abi_size > 8) return self.fail("TODO implement genMulDivBinOp from {} to {}", .{ |
| 4443 | src_ty.fmt(self.bin_file.options.module.?), |
| 4444 | dst_ty.fmt(self.bin_file.options.module.?), |
| 4445 | }); |
| 4446 | const ty = if (dst_abi_size <= 8) dst_ty else src_ty; |
| 4447 | const abi_size = if (dst_abi_size <= 8) dst_abi_size else src_abi_size; |
| 4289 | 4448 | |
| 4290 | 4449 | assert(self.register_manager.isRegFree(.rax)); |
| 4291 | 4450 | assert(self.register_manager.isRegFree(.rdx)); |
| ... | ... | @@ -4293,9 +4452,7 @@ fn genMulDivBinOp( |
| 4293 | 4452 | const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx }); |
| 4294 | 4453 | defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 4295 | 4454 | |
| 4296 | | const int_info = ty.intInfo(self.target.*); |
| 4297 | | const signedness = int_info.signedness; |
| 4298 | | |
| 4455 | const signedness = ty.intInfo(self.target.*).signedness; |
| 4299 | 4456 | switch (tag) { |
| 4300 | 4457 | .mul, |
| 4301 | 4458 | .mulwrap, |
| ... | ... | @@ -4303,11 +4460,12 @@ fn genMulDivBinOp( |
| 4303 | 4460 | .div_trunc, |
| 4304 | 4461 | .div_exact, |
| 4305 | 4462 | => { |
| 4306 | | const track_inst_rax: ?Air.Inst.Index = switch (tag) { |
| 4307 | | .mul, .mulwrap, .div_exact, .div_trunc => maybe_inst, |
| 4463 | const track_inst_rax = switch (tag) { |
| 4464 | .mul, .mulwrap => if (dst_abi_size <= 8) maybe_inst else null, |
| 4465 | .div_exact, .div_trunc => maybe_inst, |
| 4308 | 4466 | else => null, |
| 4309 | 4467 | }; |
| 4310 | | const track_inst_rdx: ?Air.Inst.Index = switch (tag) { |
| 4468 | const track_inst_rdx = switch (tag) { |
| 4311 | 4469 | .rem => maybe_inst, |
| 4312 | 4470 | else => null, |
| 4313 | 4471 | }; |
| ... | ... | @@ -4327,13 +4485,24 @@ fn genMulDivBinOp( |
| 4327 | 4485 | }, |
| 4328 | 4486 | }; |
| 4329 | 4487 | |
| 4330 | | try self.genIntMulDivOpMir(mir_tag, ty, .signed, lhs, rhs); |
| 4488 | try self.genIntMulDivOpMir(mir_tag, ty, lhs, rhs); |
| 4331 | 4489 | |
| 4332 | | return .{ .register = registerAlias(switch (tag) { |
| 4490 | if (dst_abi_size <= 8) return .{ .register = registerAlias(switch (tag) { |
| 4333 | 4491 | .mul, .mulwrap, .div_trunc, .div_exact => .rax, |
| 4334 | 4492 | .rem => .rdx, |
| 4335 | 4493 | else => unreachable, |
| 4336 | | }, abi_size) }; |
| 4494 | }, dst_abi_size) }; |
| 4495 | |
| 4496 | const dst_mcv = try self.allocRegOrMemAdvanced(dst_ty, maybe_inst, false); |
| 4497 | try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{ |
| 4498 | .base = .rbp, |
| 4499 | .disp = 0 - dst_mcv.stack_offset, |
| 4500 | }), .rax); |
| 4501 | try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{ |
| 4502 | .base = .rbp, |
| 4503 | .disp = 8 - dst_mcv.stack_offset, |
| 4504 | }), .rdx); |
| 4505 | return dst_mcv; |
| 4337 | 4506 | }, |
| 4338 | 4507 | |
| 4339 | 4508 | .mod => { |
| ... | ... | @@ -4356,7 +4525,7 @@ fn genMulDivBinOp( |
| 4356 | 4525 | return result; |
| 4357 | 4526 | }, |
| 4358 | 4527 | .unsigned => { |
| 4359 | | try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, rhs); |
| 4528 | try self.genIntMulDivOpMir(.div, ty, lhs, rhs); |
| 4360 | 4529 | return .{ .register = registerAlias(.rdx, abi_size) }; |
| 4361 | 4530 | }, |
| 4362 | 4531 | } |
| ... | ... | @@ -4395,18 +4564,13 @@ fn genMulDivBinOp( |
| 4395 | 4564 | }; |
| 4396 | 4565 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 4397 | 4566 | |
| 4398 | | const result: MCValue = result: { |
| 4399 | | switch (signedness) { |
| 4400 | | .signed => break :result try self.genInlineIntDivFloor(ty, lhs, actual_rhs), |
| 4401 | | .unsigned => { |
| 4402 | | try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, actual_rhs); |
| 4403 | | break :result MCValue{ |
| 4404 | | .register = registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))), |
| 4405 | | }; |
| 4406 | | }, |
| 4407 | | } |
| 4408 | | }; |
| 4409 | | return result; |
| 4567 | switch (signedness) { |
| 4568 | .signed => return try self.genInlineIntDivFloor(ty, lhs, actual_rhs), |
| 4569 | .unsigned => { |
| 4570 | try self.genIntMulDivOpMir(.div, ty, lhs, actual_rhs); |
| 4571 | return .{ .register = registerAlias(.rax, abi_size) }; |
| 4572 | }, |
| 4573 | } |
| 4410 | 4574 | }, |
| 4411 | 4575 | |
| 4412 | 4576 | else => unreachable, |
| ... | ... | @@ -4454,7 +4618,7 @@ fn genBinOp( |
| 4454 | 4618 | |
| 4455 | 4619 | else => false, |
| 4456 | 4620 | }; |
| 4457 | | const needs_reg_dst = switch (tag) { |
| 4621 | const dst_mem_ok = switch (tag) { |
| 4458 | 4622 | .add, |
| 4459 | 4623 | .addwrap, |
| 4460 | 4624 | .sub, |
| ... | ... | @@ -4464,9 +4628,9 @@ fn genBinOp( |
| 4464 | 4628 | .div_exact, |
| 4465 | 4629 | .div_trunc, |
| 4466 | 4630 | .div_floor, |
| 4467 | | => lhs_ty.isRuntimeFloat(), |
| 4631 | => !lhs_ty.isRuntimeFloat(), |
| 4468 | 4632 | |
| 4469 | | else => false, |
| 4633 | else => true, |
| 4470 | 4634 | }; |
| 4471 | 4635 | |
| 4472 | 4636 | const lhs_lock: ?RegisterLock = switch (lhs) { |
| ... | ... | @@ -4482,18 +4646,21 @@ fn genBinOp( |
| 4482 | 4646 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 4483 | 4647 | |
| 4484 | 4648 | var flipped: bool = false; |
| 4485 | | const dst_mcv: MCValue = blk: { |
| 4649 | const dst_mcv: MCValue = dst: { |
| 4486 | 4650 | if (maybe_inst) |inst| { |
| 4487 | | if ((!needs_reg_dst or lhs.isRegister()) and self.reuseOperand(inst, lhs_air, 0, lhs)) { |
| 4488 | | break :blk lhs; |
| 4651 | if ((dst_mem_ok or lhs.isRegister()) and self.reuseOperand(inst, lhs_air, 0, lhs)) { |
| 4652 | break :dst lhs; |
| 4489 | 4653 | } |
| 4490 | | if (is_commutative and (!needs_reg_dst or rhs.isRegister()) and self.reuseOperand(inst, rhs_air, 1, rhs)) { |
| 4654 | if (is_commutative and (dst_mem_ok or rhs.isRegister()) and |
| 4655 | self.reuseOperand(inst, rhs_air, 1, rhs)) |
| 4656 | { |
| 4491 | 4657 | flipped = true; |
| 4492 | | break :blk rhs; |
| 4658 | break :dst rhs; |
| 4493 | 4659 | } |
| 4494 | | break :blk try self.copyToRegisterWithInstTracking(inst, lhs_ty, lhs); |
| 4495 | 4660 | } |
| 4496 | | break :blk MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; |
| 4661 | const dst_mcv = try self.allocRegOrMemAdvanced(lhs_ty, maybe_inst, true); |
| 4662 | try self.setRegOrMem(lhs_ty, dst_mcv, lhs); |
| 4663 | break :dst dst_mcv; |
| 4497 | 4664 | }; |
| 4498 | 4665 | const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) { |
| 4499 | 4666 | .register => |reg| self.register_manager.lockReg(reg), |
| ... | ... | @@ -4501,17 +4668,7 @@ fn genBinOp( |
| 4501 | 4668 | }; |
| 4502 | 4669 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); |
| 4503 | 4670 | |
| 4504 | | const src_mcv: MCValue = blk: { |
| 4505 | | const mcv = if (flipped) lhs else rhs; |
| 4506 | | if (mcv.isRegister() or mcv.isMemory()) break :blk mcv; |
| 4507 | | break :blk MCValue{ .register = try self.copyToTmpRegister(rhs_ty, mcv) }; |
| 4508 | | }; |
| 4509 | | const src_mcv_lock: ?RegisterLock = switch (src_mcv) { |
| 4510 | | .register => |reg| self.register_manager.lockReg(reg), |
| 4511 | | else => null, |
| 4512 | | }; |
| 4513 | | defer if (src_mcv_lock) |lock| self.register_manager.unlockReg(lock); |
| 4514 | | |
| 4671 | const src_mcv = if (flipped) lhs else rhs; |
| 4515 | 4672 | switch (tag) { |
| 4516 | 4673 | .add, |
| 4517 | 4674 | .addwrap, |
| ... | ... | @@ -4573,14 +4730,18 @@ fn genBinOp( |
| 4573 | 4730 | .ptr_add, |
| 4574 | 4731 | .ptr_sub, |
| 4575 | 4732 | => { |
| 4576 | | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 4733 | const tmp_reg = try self.copyToTmpRegister(rhs_ty, src_mcv); |
| 4734 | const tmp_mcv = MCValue{ .register = tmp_reg }; |
| 4735 | const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 4736 | defer self.register_manager.unlockReg(tmp_lock); |
| 4737 | |
| 4738 | const elem_size = lhs_ty.elemType2().abiSize(self.target.*); |
| 4739 | try self.genIntMulComplexOpMir(rhs_ty, tmp_mcv, .{ .immediate = elem_size }); |
| 4740 | try self.genBinOpMir(switch (tag) { |
| 4577 | 4741 | .ptr_add => .add, |
| 4578 | 4742 | .ptr_sub => .sub, |
| 4579 | 4743 | else => unreachable, |
| 4580 | | }; |
| 4581 | | const elem_size = lhs_ty.elemType2().abiSize(self.target.*); |
| 4582 | | try self.genIntMulComplexOpMir(rhs_ty, src_mcv, .{ .immediate = elem_size }); |
| 4583 | | try self.genBinOpMir(mir_tag, lhs_ty, dst_mcv, src_mcv); |
| 4744 | }, lhs_ty, dst_mcv, tmp_mcv); |
| 4584 | 4745 | }, |
| 4585 | 4746 | |
| 4586 | 4747 | .bool_or, |
| ... | ... | @@ -4657,16 +4818,8 @@ fn genBinOp( |
| 4657 | 4818 | const addr_reg = (try self.register_manager.allocReg(null, gp)).to64(); |
| 4658 | 4819 | const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); |
| 4659 | 4820 | defer self.register_manager.unlockReg(addr_reg_lock); |
| 4660 | | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, mat_src_mcv); |
| 4661 | | |
| 4662 | | // To get the actual address of the value we want to modify we |
| 4663 | | // we have to go through the GOT |
| 4664 | | try self.asmRegisterMemory( |
| 4665 | | .mov, |
| 4666 | | addr_reg, |
| 4667 | | Memory.sib(.qword, .{ .base = addr_reg }), |
| 4668 | | ); |
| 4669 | 4821 | |
| 4822 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, mat_src_mcv); |
| 4670 | 4823 | try self.asmCmovccRegisterMemory( |
| 4671 | 4824 | registerAlias(tmp_reg, abi_size), |
| 4672 | 4825 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = addr_reg }), |
| ... | ... | @@ -4706,6 +4859,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 4706 | 4859 | .eflags => unreachable, |
| 4707 | 4860 | .register_overflow => unreachable, |
| 4708 | 4861 | .register => |dst_reg| { |
| 4862 | const dst_alias = registerAlias(dst_reg, abi_size); |
| 4709 | 4863 | switch (src_mcv) { |
| 4710 | 4864 | .none => unreachable, |
| 4711 | 4865 | .undef => unreachable, |
| ... | ... | @@ -4728,41 +4882,47 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 4728 | 4882 | }, |
| 4729 | 4883 | else => try self.asmRegisterRegister( |
| 4730 | 4884 | mir_tag, |
| 4731 | | registerAlias(dst_reg, abi_size), |
| 4885 | dst_alias, |
| 4732 | 4886 | registerAlias(src_reg, abi_size), |
| 4733 | 4887 | ), |
| 4734 | 4888 | }, |
| 4735 | 4889 | .immediate => |imm| { |
| 4736 | 4890 | switch (self.regBitSize(ty)) { |
| 4737 | | 8, 16, 32 => { |
| 4738 | | try self.asmRegisterImmediate( |
| 4739 | | mir_tag, |
| 4740 | | registerAlias(dst_reg, abi_size), |
| 4891 | 8 => try self.asmRegisterImmediate( |
| 4892 | mir_tag, |
| 4893 | dst_alias, |
| 4894 | if (math.cast(i8, @bitCast(i64, imm))) |small| |
| 4895 | Immediate.s(small) |
| 4896 | else |
| 4897 | Immediate.u(@intCast(u8, imm)), |
| 4898 | ), |
| 4899 | 16 => try self.asmRegisterImmediate( |
| 4900 | mir_tag, |
| 4901 | dst_alias, |
| 4902 | if (math.cast(i16, @bitCast(i64, imm))) |small| |
| 4903 | Immediate.s(small) |
| 4904 | else |
| 4905 | Immediate.u(@intCast(u16, imm)), |
| 4906 | ), |
| 4907 | 32 => try self.asmRegisterImmediate( |
| 4908 | mir_tag, |
| 4909 | dst_alias, |
| 4910 | if (math.cast(i32, @bitCast(i64, imm))) |small| |
| 4911 | Immediate.s(small) |
| 4912 | else |
| 4741 | 4913 | Immediate.u(@intCast(u32, imm)), |
| 4742 | | ); |
| 4743 | | }, |
| 4744 | | 64 => { |
| 4745 | | if (math.cast(i32, @bitCast(i64, imm))) |small| { |
| 4746 | | try self.asmRegisterImmediate( |
| 4747 | | mir_tag, |
| 4748 | | registerAlias(dst_reg, abi_size), |
| 4749 | | Immediate.s(small), |
| 4750 | | ); |
| 4751 | | } else { |
| 4752 | | try self.asmRegisterRegister( |
| 4753 | | mir_tag, |
| 4754 | | registerAlias(dst_reg, abi_size), |
| 4755 | | registerAlias(try self.copyToTmpRegister(ty, src_mcv), abi_size), |
| 4756 | | ); |
| 4757 | | } |
| 4758 | | }, |
| 4759 | | else => return self.fail("TODO genBinOpMir implement large immediate ABI", .{}), |
| 4914 | ), |
| 4915 | 64 => if (math.cast(i32, @bitCast(i64, imm))) |small| |
| 4916 | try self.asmRegisterImmediate(mir_tag, dst_alias, Immediate.s(small)) |
| 4917 | else |
| 4918 | try self.asmRegisterRegister(mir_tag, dst_alias, registerAlias( |
| 4919 | try self.copyToTmpRegister(ty, src_mcv), |
| 4920 | abi_size, |
| 4921 | )), |
| 4922 | else => unreachable, |
| 4760 | 4923 | } |
| 4761 | 4924 | }, |
| 4762 | | .memory, |
| 4763 | | .linker_load, |
| 4764 | | .eflags, |
| 4765 | | => { |
| 4925 | .memory, .linker_load, .eflags => { |
| 4766 | 4926 | assert(abi_size <= 8); |
| 4767 | 4927 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); |
| 4768 | 4928 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -4779,7 +4939,27 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 4779 | 4939 | }, |
| 4780 | 4940 | } |
| 4781 | 4941 | }, |
| 4782 | | .ptr_stack_offset, .stack_offset => |dst_off| { |
| 4942 | .memory, .linker_load, .stack_offset => { |
| 4943 | const dst: ?struct { |
| 4944 | addr_reg: Register, |
| 4945 | addr_lock: RegisterLock, |
| 4946 | } = switch (dst_mcv) { |
| 4947 | else => unreachable, |
| 4948 | .memory, .linker_load => dst: { |
| 4949 | const dst_addr_reg = try self.register_manager.allocReg(null, gp); |
| 4950 | const dst_addr_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); |
| 4951 | errdefer self.register_manager.unlockReg(dst_addr_lock); |
| 4952 | |
| 4953 | try self.loadMemPtrIntoRegister(dst_addr_reg, Type.usize, dst_mcv); |
| 4954 | break :dst .{ |
| 4955 | .addr_reg = dst_addr_reg, |
| 4956 | .addr_lock = dst_addr_lock, |
| 4957 | }; |
| 4958 | }, |
| 4959 | .stack_offset => null, |
| 4960 | }; |
| 4961 | defer if (dst) |lock| self.register_manager.unlockReg(lock.addr_lock); |
| 4962 | |
| 4783 | 4963 | const src: ?struct { |
| 4784 | 4964 | limb_reg: Register, |
| 4785 | 4965 | limb_lock: RegisterLock, |
| ... | ... | @@ -4787,7 +4967,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 4787 | 4967 | addr_lock: RegisterLock, |
| 4788 | 4968 | } = switch (src_mcv) { |
| 4789 | 4969 | else => null, |
| 4790 | | .memory, .linker_load => addr: { |
| 4970 | .memory, .linker_load => src: { |
| 4791 | 4971 | const src_limb_reg = try self.register_manager.allocReg(null, gp); |
| 4792 | 4972 | const src_limb_lock = self.register_manager.lockRegAssumeUnused(src_limb_reg); |
| 4793 | 4973 | errdefer self.register_manager.unlockReg(src_limb_lock); |
| ... | ... | @@ -4797,15 +4977,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 4797 | 4977 | errdefer self.register_manager.unlockReg(src_addr_lock); |
| 4798 | 4978 | |
| 4799 | 4979 | try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_mcv); |
| 4800 | | // To get the actual address of the value we want to modify we |
| 4801 | | // we have to go through the GOT |
| 4802 | | try self.asmRegisterMemory( |
| 4803 | | .mov, |
| 4804 | | src_addr_reg, |
| 4805 | | Memory.sib(.qword, .{ .base = src_addr_reg }), |
| 4806 | | ); |
| 4807 | | |
| 4808 | | break :addr .{ |
| 4980 | break :src .{ |
| 4809 | 4981 | .addr_reg = src_addr_reg, |
| 4810 | 4982 | .addr_lock = src_addr_lock, |
| 4811 | 4983 | .limb_reg = src_limb_reg, |
| ... | ... | @@ -4831,7 +5003,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 4831 | 5003 | 0 => mir_tag, |
| 4832 | 5004 | else => switch (mir_tag) { |
| 4833 | 5005 | .add => .adc, |
| 4834 | | .sub => .sbb, |
| 5006 | .sub, .cmp => .sbb, |
| 4835 | 5007 | .@"or", .@"and", .xor => mir_tag, |
| 4836 | 5008 | else => return self.fail("TODO genBinOpMir implement large ABI for {s}", .{ |
| 4837 | 5009 | @tagName(mir_tag), |
| ... | ... | @@ -4840,7 +5012,14 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 4840 | 5012 | }; |
| 4841 | 5013 | const dst_limb_mem = Memory.sib( |
| 4842 | 5014 | Memory.PtrSize.fromSize(limb_abi_size), |
| 4843 | | .{ .base = .rbp, .disp = off - dst_off }, |
| 5015 | switch (dst_mcv) { |
| 5016 | else => unreachable, |
| 5017 | .stack_offset => |dst_off| .{ |
| 5018 | .base = .rbp, |
| 5019 | .disp = off - dst_off, |
| 5020 | }, |
| 5021 | .memory, .linker_load => .{ .base = dst.?.addr_reg, .disp = off }, |
| 5022 | }, |
| 4844 | 5023 | ); |
| 4845 | 5024 | switch (src_mcv) { |
| 4846 | 5025 | .none => unreachable, |
| ... | ... | @@ -4861,34 +5040,45 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 4861 | 5040 | .unsigned => 0, |
| 4862 | 5041 | }; |
| 4863 | 5042 | switch (self.regBitSize(limb_ty)) { |
| 4864 | | 8, 16, 32 => { |
| 5043 | 8 => try self.asmMemoryImmediate( |
| 5044 | mir_limb_tag, |
| 5045 | dst_limb_mem, |
| 5046 | if (math.cast(i8, @bitCast(i64, imm))) |small| |
| 5047 | Immediate.s(small) |
| 5048 | else |
| 5049 | Immediate.u(@intCast(u8, imm)), |
| 5050 | ), |
| 5051 | 16 => try self.asmMemoryImmediate( |
| 5052 | mir_limb_tag, |
| 5053 | dst_limb_mem, |
| 5054 | if (math.cast(i16, @bitCast(i64, imm))) |small| |
| 5055 | Immediate.s(small) |
| 5056 | else |
| 5057 | Immediate.u(@intCast(u16, imm)), |
| 5058 | ), |
| 5059 | 32 => try self.asmMemoryImmediate( |
| 5060 | mir_limb_tag, |
| 5061 | dst_limb_mem, |
| 5062 | if (math.cast(i32, @bitCast(i64, imm))) |small| |
| 5063 | Immediate.s(small) |
| 5064 | else |
| 5065 | Immediate.u(@intCast(u32, imm)), |
| 5066 | ), |
| 5067 | 64 => if (math.cast(i32, @bitCast(i64, imm))) |small| |
| 4865 | 5068 | try self.asmMemoryImmediate( |
| 4866 | 5069 | mir_limb_tag, |
| 4867 | 5070 | dst_limb_mem, |
| 4868 | | if (math.cast(i32, @bitCast(i64, imm))) |small| |
| 4869 | | Immediate.s(small) |
| 4870 | | else |
| 4871 | | Immediate.u(@intCast(u32, imm)), |
| 4872 | | ); |
| 4873 | | }, |
| 4874 | | 64 => { |
| 4875 | | if (math.cast(i32, @bitCast(i64, imm))) |small| { |
| 4876 | | try self.asmMemoryImmediate( |
| 4877 | | mir_limb_tag, |
| 4878 | | dst_limb_mem, |
| 4879 | | Immediate.s(small), |
| 4880 | | ); |
| 4881 | | } else { |
| 4882 | | try self.asmMemoryRegister( |
| 4883 | | mir_limb_tag, |
| 4884 | | dst_limb_mem, |
| 4885 | | registerAlias( |
| 4886 | | try self.copyToTmpRegister(limb_ty, .{ .immediate = imm }), |
| 4887 | | limb_abi_size, |
| 4888 | | ), |
| 4889 | | ); |
| 4890 | | } |
| 4891 | | }, |
| 5071 | Immediate.s(small), |
| 5072 | ) |
| 5073 | else |
| 5074 | try self.asmMemoryRegister( |
| 5075 | mir_limb_tag, |
| 5076 | dst_limb_mem, |
| 5077 | registerAlias( |
| 5078 | try self.copyToTmpRegister(limb_ty, .{ .immediate = imm }), |
| 5079 | limb_abi_size, |
| 5080 | ), |
| 5081 | ), |
| 4892 | 5082 | else => unreachable, |
| 4893 | 5083 | } |
| 4894 | 5084 | }, |
| ... | ... | @@ -4930,12 +5120,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 4930 | 5120 | } |
| 4931 | 5121 | } |
| 4932 | 5122 | }, |
| 4933 | | .memory => { |
| 4934 | | return self.fail("TODO implement x86 genBinOpMir destination memory", .{}); |
| 4935 | | }, |
| 4936 | | .linker_load => { |
| 4937 | | return self.fail("TODO implement x86 genBinOpMir destination symbol at index", .{}); |
| 4938 | | }, |
| 5123 | .ptr_stack_offset => unreachable, |
| 4939 | 5124 | } |
| 4940 | 5125 | } |
| 4941 | 5126 | |
| ... | ... | @@ -5191,7 +5376,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 5191 | 5376 | else => unreachable, |
| 5192 | 5377 | }; |
| 5193 | 5378 | |
| 5194 | | var info = try self.resolveCallingConventionValues(fn_ty); |
| 5379 | var info = try self.resolveCallingConventionValues(fn_ty, args[fn_ty.fnParamLen()..]); |
| 5195 | 5380 | defer info.deinit(self); |
| 5196 | 5381 | |
| 5197 | 5382 | try self.spillEflagsIfOccupied(); |
| ... | ... | @@ -5280,8 +5465,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 5280 | 5465 | |
| 5281 | 5466 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 5282 | 5467 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); |
| 5283 | | const atom = elf_file.getAtom(atom_index); |
| 5284 | | const got_addr = atom.getOffsetTableAddress(elf_file); |
| 5468 | const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file); |
| 5285 | 5469 | try self.asmMemory(.call, Memory.sib(.qword, .{ |
| 5286 | 5470 | .base = .ds, |
| 5287 | 5471 | .disp = @intCast(i32, got_addr), |
| ... | ... | @@ -5289,22 +5473,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 5289 | 5473 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 5290 | 5474 | const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl); |
| 5291 | 5475 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; |
| 5292 | | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 5293 | | .linker_load = .{ |
| 5294 | | .type = .got, |
| 5295 | | .sym_index = sym_index, |
| 5296 | | }, |
| 5297 | | }); |
| 5476 | try self.genSetReg(Type.usize, .rax, .{ .linker_load = .{ |
| 5477 | .type = .got, |
| 5478 | .sym_index = sym_index, |
| 5479 | } }); |
| 5298 | 5480 | try self.asmRegister(.call, .rax); |
| 5299 | 5481 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 5300 | 5482 | const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl); |
| 5301 | 5483 | const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; |
| 5302 | | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 5303 | | .linker_load = .{ |
| 5304 | | .type = .got, |
| 5305 | | .sym_index = sym_index, |
| 5306 | | }, |
| 5307 | | }); |
| 5484 | try self.genSetReg(Type.usize, .rax, .{ .linker_load = .{ |
| 5485 | .type = .got, |
| 5486 | .sym_index = sym_index, |
| 5487 | } }); |
| 5308 | 5488 | try self.asmRegister(.call, .rax); |
| 5309 | 5489 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 5310 | 5490 | const decl_block_index = try p9.seeDecl(func.owner_decl); |
| ... | ... | @@ -5325,7 +5505,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 5325 | 5505 | const lib_name = mem.sliceTo(extern_fn.lib_name, 0); |
| 5326 | 5506 | if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 5327 | 5507 | const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name); |
| 5328 | | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 5508 | try self.genSetReg(Type.usize, .rax, .{ |
| 5329 | 5509 | .linker_load = .{ |
| 5330 | 5510 | .type = .import, |
| 5331 | 5511 | .sym_index = sym_index, |
| ... | ... | @@ -5353,7 +5533,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 5353 | 5533 | } else { |
| 5354 | 5534 | assert(ty.zigTypeTag() == .Pointer); |
| 5355 | 5535 | const mcv = try self.resolveInst(callee); |
| 5356 | | try self.genSetReg(Type.initTag(.usize), .rax, mcv); |
| 5536 | try self.genSetReg(Type.usize, .rax, mcv); |
| 5357 | 5537 | try self.asmRegister(.call, .rax); |
| 5358 | 5538 | } |
| 5359 | 5539 | |
| ... | ... | @@ -5457,79 +5637,61 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 5457 | 5637 | |
| 5458 | 5638 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 5459 | 5639 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 5640 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 5641 | const ty = self.air.typeOf(bin_op.lhs); |
| 5642 | const ty_abi_size = ty.abiSize(self.target.*); |
| 5643 | const can_reuse = ty_abi_size <= 8; |
| 5460 | 5644 | |
| 5461 | | if (self.liveness.isUnused(inst)) { |
| 5462 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 5463 | | } |
| 5464 | | |
| 5465 | | const ty = self.air.typeOf(bin_op.lhs); |
| 5466 | | const abi_size = ty.abiSize(self.target.*); |
| 5467 | | if (abi_size > 8) return self.fail("TODO implement cmp for large values", .{}); |
| 5468 | | |
| 5469 | | const signedness: std.builtin.Signedness = blk: { |
| 5470 | | // For non-int types, we treat the values as unsigned |
| 5471 | | if (ty.zigTypeTag() != .Int) break :blk .unsigned; |
| 5472 | | |
| 5473 | | // Otherwise, we take the signedness of the actual int |
| 5474 | | break :blk ty.intInfo(self.target.*).signedness; |
| 5475 | | }; |
| 5476 | | |
| 5477 | | try self.spillEflagsIfOccupied(); |
| 5478 | | self.eflags_inst = inst; |
| 5645 | try self.spillEflagsIfOccupied(); |
| 5646 | self.eflags_inst = inst; |
| 5479 | 5647 | |
| 5480 | | const result: MCValue = result: { |
| 5481 | | // There are 2 operands, destination and source. |
| 5482 | | // Either one, but not both, can be a memory operand. |
| 5483 | | // Source operand can be an immediate, 8 bits or 32 bits. |
| 5484 | | // TODO look into reusing the operand |
| 5485 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 5486 | | const lhs_lock: ?RegisterLock = switch (lhs) { |
| 5648 | const lhs_mcv = try self.resolveInst(bin_op.lhs); |
| 5649 | const lhs_lock = switch (lhs_mcv) { |
| 5487 | 5650 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 5488 | 5651 | else => null, |
| 5489 | 5652 | }; |
| 5490 | 5653 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 5491 | 5654 | |
| 5492 | | const dst_reg = try self.copyToTmpRegister(ty, lhs); |
| 5493 | | const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg); |
| 5494 | | defer self.register_manager.unlockReg(dst_reg_lock); |
| 5495 | | |
| 5496 | | const dst_mcv = MCValue{ .register = dst_reg }; |
| 5497 | | |
| 5498 | | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 5499 | | // This instruction supports only signed 32-bit immediates at most. |
| 5500 | | const src_mcv: MCValue = blk: { |
| 5501 | | switch (rhs_ty.zigTypeTag()) { |
| 5502 | | .Float => { |
| 5503 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 5504 | | const rhs_lock: ?RegisterLock = switch (rhs) { |
| 5505 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 5506 | | else => null, |
| 5507 | | }; |
| 5508 | | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 5509 | | const src_reg = try self.copyToTmpRegister(rhs_ty, rhs); |
| 5510 | | break :blk MCValue{ .register = src_reg }; |
| 5511 | | }, |
| 5512 | | else => break :blk try self.limitImmediateType(bin_op.rhs, i32), |
| 5513 | | } |
| 5655 | const rhs_mcv = try self.resolveInst(bin_op.rhs); |
| 5656 | const rhs_lock = switch (rhs_mcv) { |
| 5657 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 5658 | else => null, |
| 5514 | 5659 | }; |
| 5515 | | const src_lock: ?RegisterLock = switch (src_mcv) { |
| 5660 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 5661 | |
| 5662 | const dst_mem_ok = !ty.isRuntimeFloat(); |
| 5663 | var flipped = false; |
| 5664 | const dst_mcv: MCValue = if (can_reuse and !lhs_mcv.isImmediate() and |
| 5665 | (dst_mem_ok or lhs_mcv.isRegister()) and self.liveness.operandDies(inst, 0)) |
| 5666 | lhs_mcv |
| 5667 | else if (can_reuse and !rhs_mcv.isImmediate() and |
| 5668 | (dst_mem_ok or rhs_mcv.isRegister()) and self.liveness.operandDies(inst, 1)) |
| 5669 | dst: { |
| 5670 | flipped = true; |
| 5671 | break :dst rhs_mcv; |
| 5672 | } else if (dst_mem_ok) dst: { |
| 5673 | const dst_mcv = try self.allocTempRegOrMem(ty, true); |
| 5674 | try self.setRegOrMem(ty, dst_mcv, lhs_mcv); |
| 5675 | break :dst dst_mcv; |
| 5676 | } else .{ .register = try self.copyToTmpRegister(ty, lhs_mcv) }; |
| 5677 | const dst_lock = switch (dst_mcv) { |
| 5516 | 5678 | .register => |reg| self.register_manager.lockReg(reg), |
| 5517 | 5679 | else => null, |
| 5518 | 5680 | }; |
| 5519 | | defer if (src_lock) |lock| self.register_manager.unlockReg(lock); |
| 5681 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 5520 | 5682 | |
| 5683 | const src_mcv = if (flipped) lhs_mcv else rhs_mcv; |
| 5521 | 5684 | try self.genBinOpMir(switch (ty.tag()) { |
| 5522 | 5685 | else => .cmp, |
| 5523 | 5686 | .f32 => .ucomiss, |
| 5524 | 5687 | .f64 => .ucomisd, |
| 5525 | 5688 | }, ty, dst_mcv, src_mcv); |
| 5526 | 5689 | |
| 5527 | | break :result switch (signedness) { |
| 5528 | | .signed => MCValue{ .eflags = Condition.fromCompareOperatorSigned(op) }, |
| 5529 | | .unsigned => MCValue{ .eflags = Condition.fromCompareOperatorUnsigned(op) }, |
| 5690 | const signedness = if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned; |
| 5691 | break :result .{ |
| 5692 | .eflags = Condition.fromCompareOperator(signedness, if (flipped) op.reverse() else op), |
| 5530 | 5693 | }; |
| 5531 | 5694 | }; |
| 5532 | | |
| 5533 | 5695 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 5534 | 5696 | } |
| 5535 | 5697 | |
| ... | ... | @@ -5790,13 +5952,6 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC |
| 5790 | 5952 | |
| 5791 | 5953 | try self.loadMemPtrIntoRegister(addr_reg, Type.usize, opt_mcv); |
| 5792 | 5954 | |
| 5793 | | // To get the actual address of the value we want to modify we have to go through the GOT |
| 5794 | | try self.asmRegisterMemory( |
| 5795 | | .mov, |
| 5796 | | addr_reg, |
| 5797 | | Memory.sib(.qword, .{ .base = addr_reg }), |
| 5798 | | ); |
| 5799 | | |
| 5800 | 5955 | const some_abi_size = @intCast(u32, some_info.ty.abiSize(self.target.*)); |
| 5801 | 5956 | try self.asmMemoryImmediate(.cmp, Memory.sib( |
| 5802 | 5957 | Memory.PtrSize.fromSize(some_abi_size), |
| ... | ... | @@ -6681,6 +6836,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 6681 | 6836 | } |
| 6682 | 6837 | |
| 6683 | 6838 | fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: InlineMemcpyOpts) InnerError!void { |
| 6839 | const base_reg = opts.dest_stack_base orelse .rbp; |
| 6684 | 6840 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 6685 | 6841 | switch (mcv) { |
| 6686 | 6842 | .dead => unreachable, |
| ... | ... | @@ -6696,12 +6852,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 6696 | 6852 | 4 => 0xaaaaaaaa, |
| 6697 | 6853 | else => unreachable, |
| 6698 | 6854 | }; |
| 6699 | | return self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 6700 | | .base = opts.dest_stack_base orelse .rbp, |
| 6701 | | .disp = -stack_offset, |
| 6702 | | }), Immediate.u(value)); |
| 6855 | return self.asmMemoryImmediate(.mov, Memory.sib( |
| 6856 | Memory.PtrSize.fromSize(abi_size), |
| 6857 | .{ .base = base_reg, .disp = -stack_offset }, |
| 6858 | ), Immediate.u(value)); |
| 6703 | 6859 | }, |
| 6704 | | 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }, opts), |
| 6860 | 8 => return self.genSetStack( |
| 6861 | ty, |
| 6862 | stack_offset, |
| 6863 | .{ .immediate = 0xaaaaaaaaaaaaaaaa }, |
| 6864 | opts, |
| 6865 | ), |
| 6705 | 6866 | else => |x| return self.genInlineMemset( |
| 6706 | 6867 | .{ .stack_offset = stack_offset }, |
| 6707 | 6868 | .{ .immediate = 0xaa }, |
| ... | ... | @@ -6729,12 +6890,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 6729 | 6890 | .{}, |
| 6730 | 6891 | ); |
| 6731 | 6892 | }, |
| 6732 | | .eflags => { |
| 6733 | | const reg = try self.copyToTmpRegister(ty, mcv); |
| 6734 | | return self.genSetStack(ty, stack_offset, .{ .register = reg }, opts); |
| 6735 | | }, |
| 6736 | | .immediate => |x_big| { |
| 6737 | | const base_reg = opts.dest_stack_base orelse .rbp; |
| 6893 | .eflags => |cc| try self.asmSetccMemory( |
| 6894 | Memory.sib(.byte, .{ .base = base_reg, .disp = -stack_offset }), |
| 6895 | cc, |
| 6896 | ), |
| 6897 | .immediate => |imm| { |
| 6738 | 6898 | // TODO |
| 6739 | 6899 | switch (abi_size) { |
| 6740 | 6900 | 0 => { |
| ... | ... | @@ -6742,13 +6902,13 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 6742 | 6902 | try self.asmMemoryImmediate(.mov, Memory.sib(.byte, .{ |
| 6743 | 6903 | .base = base_reg, |
| 6744 | 6904 | .disp = -stack_offset, |
| 6745 | | }), Immediate.u(@truncate(u8, x_big))); |
| 6905 | }), Immediate.u(@truncate(u8, imm))); |
| 6746 | 6906 | }, |
| 6747 | 6907 | 1, 2, 4 => { |
| 6748 | 6908 | const immediate = if (ty.isSignedInt()) |
| 6749 | | Immediate.s(@truncate(i32, @bitCast(i64, x_big))) |
| 6909 | Immediate.s(@truncate(i32, @bitCast(i64, imm))) |
| 6750 | 6910 | else |
| 6751 | | Immediate.u(@intCast(u32, x_big)); |
| 6911 | Immediate.u(@intCast(u32, imm)); |
| 6752 | 6912 | try self.asmMemoryImmediate(.mov, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ |
| 6753 | 6913 | .base = base_reg, |
| 6754 | 6914 | .disp = -stack_offset, |
| ... | ... | @@ -6758,27 +6918,32 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 6758 | 6918 | else => { |
| 6759 | 6919 | // 64 bit write to memory would take two mov's anyways so we |
| 6760 | 6920 | // insted just use two 32 bit writes to avoid register allocation |
| 6761 | | var offset: i32 = 0; |
| 6762 | | while (offset < abi_size) : (offset += 4) try self.asmMemoryImmediate( |
| 6763 | | .mov, |
| 6764 | | Memory.sib(.dword, .{ .base = base_reg, .disp = offset - stack_offset }), |
| 6765 | | if (ty.isSignedInt()) |
| 6766 | | Immediate.s(@truncate( |
| 6767 | | i32, |
| 6768 | | @bitCast(i64, x_big) >> (math.cast(u6, offset * 8) orelse 63), |
| 6769 | | )) |
| 6770 | | else |
| 6771 | | Immediate.u(@truncate( |
| 6772 | | u32, |
| 6773 | | if (math.cast(u6, offset * 8)) |shift| x_big >> shift else 0, |
| 6774 | | )), |
| 6775 | | ); |
| 6921 | if (std.math.cast(i32, @bitCast(i64, imm))) |small| { |
| 6922 | try self.asmMemoryImmediate(.mov, Memory.sib( |
| 6923 | Memory.PtrSize.fromSize(abi_size), |
| 6924 | .{ .base = base_reg, .disp = -stack_offset }, |
| 6925 | ), Immediate.s(small)); |
| 6926 | } else { |
| 6927 | var offset: i32 = 0; |
| 6928 | while (offset < abi_size) : (offset += 4) try self.asmMemoryImmediate( |
| 6929 | .mov, |
| 6930 | Memory.sib(.dword, .{ .base = base_reg, .disp = offset - stack_offset }), |
| 6931 | if (ty.isSignedInt()) |
| 6932 | Immediate.s(@truncate( |
| 6933 | i32, |
| 6934 | @bitCast(i64, imm) >> (math.cast(u6, offset * 8) orelse 63), |
| 6935 | )) |
| 6936 | else |
| 6937 | Immediate.u(@truncate( |
| 6938 | u32, |
| 6939 | if (math.cast(u6, offset * 8)) |shift| imm >> shift else 0, |
| 6940 | )), |
| 6941 | ); |
| 6942 | } |
| 6776 | 6943 | }, |
| 6777 | 6944 | } |
| 6778 | 6945 | }, |
| 6779 | 6946 | .register => |reg| { |
| 6780 | | const base_reg = opts.dest_stack_base orelse .rbp; |
| 6781 | | |
| 6782 | 6947 | switch (ty.zigTypeTag()) { |
| 6783 | 6948 | .Float => { |
| 6784 | 6949 | if (intrinsicsAllowed(self.target.*, ty)) { |
| ... | ... | @@ -6808,22 +6973,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 6808 | 6973 | }, |
| 6809 | 6974 | } |
| 6810 | 6975 | }, |
| 6811 | | .memory, .linker_load => { |
| 6812 | | if (abi_size <= 8) { |
| 6813 | | const reg = try self.copyToTmpRegister(ty, mcv); |
| 6814 | | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }, opts); |
| 6815 | | } |
| 6816 | | |
| 6817 | | try self.genInlineMemcpy(.{ .stack_offset = stack_offset }, mcv, .{ .immediate = abi_size }, opts); |
| 6818 | | }, |
| 6819 | | .ptr_stack_offset => { |
| 6820 | | const reg = try self.copyToTmpRegister(ty, mcv); |
| 6821 | | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }, opts); |
| 6822 | | }, |
| 6823 | | .stack_offset => |off| { |
| 6824 | | if (stack_offset == off) { |
| 6825 | | // Copy stack variable to itself; nothing to do. |
| 6826 | | return; |
| 6976 | .memory, .linker_load, .stack_offset, .ptr_stack_offset => { |
| 6977 | switch (mcv) { |
| 6978 | else => unreachable, |
| 6979 | .memory, .linker_load, .ptr_stack_offset => {}, |
| 6980 | .stack_offset => |src_off| if (stack_offset == src_off) { |
| 6981 | // Copy stack variable to itself; nothing to do. |
| 6982 | return; |
| 6983 | }, |
| 6827 | 6984 | } |
| 6828 | 6985 | |
| 6829 | 6986 | if (abi_size <= 8) { |
| ... | ... | @@ -7101,69 +7258,38 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 7101 | 7258 | |
| 7102 | 7259 | try self.asmRegisterRegister(.mov, registerAlias(reg, abi_size), registerAlias(src_reg, abi_size)); |
| 7103 | 7260 | }, |
| 7104 | | .linker_load => { |
| 7105 | | switch (ty.zigTypeTag()) { |
| 7106 | | .Float => { |
| 7107 | | const base_reg = try self.register_manager.allocReg(null, gp); |
| 7108 | | try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); |
| 7109 | | |
| 7110 | | if (intrinsicsAllowed(self.target.*, ty)) { |
| 7111 | | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 7112 | | .f32 => .movss, |
| 7113 | | .f64 => .movsd, |
| 7114 | | else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), |
| 7115 | | }; |
| 7116 | | const ptr_size: Memory.PtrSize = switch (ty.tag()) { |
| 7117 | | .f32 => .dword, |
| 7118 | | .f64 => .qword, |
| 7119 | | else => unreachable, |
| 7120 | | }; |
| 7121 | | return self.asmRegisterMemory( |
| 7122 | | tag, |
| 7123 | | reg.to128(), |
| 7124 | | Memory.sib(ptr_size, .{ .base = base_reg.to64() }), |
| 7125 | | ); |
| 7126 | | } |
| 7127 | | |
| 7128 | | return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); |
| 7129 | | }, |
| 7130 | | else => { |
| 7131 | | try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); |
| 7132 | | try self.asmRegisterMemory( |
| 7133 | | .mov, |
| 7134 | | registerAlias(reg, abi_size), |
| 7135 | | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }), |
| 7136 | | ); |
| 7137 | | }, |
| 7138 | | } |
| 7139 | | }, |
| 7140 | | .memory => |x| switch (ty.zigTypeTag()) { |
| 7261 | .memory, .linker_load => switch (ty.zigTypeTag()) { |
| 7141 | 7262 | .Float => { |
| 7142 | 7263 | const base_reg = try self.register_manager.allocReg(null, gp); |
| 7143 | 7264 | try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); |
| 7144 | 7265 | |
| 7145 | 7266 | if (intrinsicsAllowed(self.target.*, ty)) { |
| 7146 | | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 7147 | | .f32 => .movss, |
| 7148 | | .f64 => .movsd, |
| 7149 | | else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), |
| 7150 | | }; |
| 7151 | | const ptr_size: Memory.PtrSize = switch (ty.tag()) { |
| 7152 | | .f32 => .dword, |
| 7153 | | .f64 => .qword, |
| 7154 | | else => unreachable, |
| 7155 | | }; |
| 7156 | 7267 | return self.asmRegisterMemory( |
| 7157 | | tag, |
| 7268 | switch (ty.tag()) { |
| 7269 | .f32 => .movss, |
| 7270 | .f64 => .movsd, |
| 7271 | else => return self.fail("TODO genSetReg from memory for {}", .{ |
| 7272 | ty.fmt(self.bin_file.options.module.?), |
| 7273 | }), |
| 7274 | }, |
| 7158 | 7275 | reg.to128(), |
| 7159 | | Memory.sib(ptr_size, .{ .base = base_reg.to64() }), |
| 7276 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = base_reg.to64() }), |
| 7160 | 7277 | ); |
| 7161 | 7278 | } |
| 7162 | 7279 | |
| 7163 | 7280 | return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); |
| 7164 | 7281 | }, |
| 7165 | | else => { |
| 7166 | | if (x <= math.maxInt(i32)) { |
| 7282 | else => switch (mcv) { |
| 7283 | else => unreachable, |
| 7284 | .linker_load => { |
| 7285 | try self.loadMemPtrIntoRegister(reg, Type.usize, mcv); |
| 7286 | try self.asmRegisterMemory( |
| 7287 | .mov, |
| 7288 | registerAlias(reg, abi_size), |
| 7289 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }), |
| 7290 | ); |
| 7291 | }, |
| 7292 | .memory => |x| if (x <= math.maxInt(i32)) { |
| 7167 | 7293 | try self.asmRegisterMemory( |
| 7168 | 7294 | .mov, |
| 7169 | 7295 | registerAlias(reg, abi_size), |
| ... | ... | @@ -7190,7 +7316,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 7190 | 7316 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = reg.to64() }), |
| 7191 | 7317 | ); |
| 7192 | 7318 | } |
| 7193 | | } |
| 7319 | }, |
| 7194 | 7320 | }, |
| 7195 | 7321 | }, |
| 7196 | 7322 | .stack_offset => |off| { |
| ... | ... | @@ -7231,15 +7357,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 7231 | 7357 | .{ty.fmtDebug()}, |
| 7232 | 7358 | ), |
| 7233 | 7359 | }; |
| 7234 | | const ptr_size: Memory.PtrSize = switch (ty.tag()) { |
| 7235 | | .f32 => .dword, |
| 7236 | | .f64 => .qword, |
| 7237 | | else => unreachable, |
| 7238 | | }; |
| 7239 | | return self.asmRegisterMemory(tag, reg.to128(), Memory.sib(ptr_size, .{ |
| 7240 | | .base = .rbp, |
| 7241 | | .disp = -off, |
| 7242 | | })); |
| 7360 | return self.asmRegisterMemory(tag, reg.to128(), Memory.sib( |
| 7361 | Memory.PtrSize.fromSize(abi_size), |
| 7362 | .{ .base = .rbp, .disp = -off }, |
| 7363 | )); |
| 7243 | 7364 | } |
| 7244 | 7365 | return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{}); |
| 7245 | 7366 | }, |
| ... | ... | @@ -7299,7 +7420,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 7299 | 7420 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { |
| 7300 | 7421 | const stack_offset = @intCast(i32, try self.allocMem(inst, 16, 16)); |
| 7301 | 7422 | try self.genSetStack(ptr_ty, stack_offset, ptr, .{}); |
| 7302 | | try self.genSetStack(Type.initTag(.u64), stack_offset - 8, .{ .immediate = array_len }, .{}); |
| 7423 | try self.genSetStack(Type.u64, stack_offset - 8, .{ .immediate = array_len }, .{}); |
| 7303 | 7424 | break :blk .{ .stack_offset = stack_offset }; |
| 7304 | 7425 | }; |
| 7305 | 7426 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | ... | @@ -7809,10 +7930,92 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 7809 | 7930 | |
| 7810 | 7931 | fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { |
| 7811 | 7932 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 7812 | | const operand = try self.resolveInst(un_op); |
| 7813 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { |
| 7814 | | _ = operand; |
| 7815 | | return self.fail("TODO implement airErrorName for x86_64", .{}); |
| 7933 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 7934 | const err_ty = self.air.typeOf(un_op); |
| 7935 | const err_mcv = try self.resolveInst(un_op); |
| 7936 | const err_reg = try self.copyToTmpRegister(err_ty, err_mcv); |
| 7937 | const err_lock = self.register_manager.lockRegAssumeUnused(err_reg); |
| 7938 | defer self.register_manager.unlockReg(err_lock); |
| 7939 | |
| 7940 | const addr_reg = try self.register_manager.allocReg(null, gp); |
| 7941 | const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); |
| 7942 | defer self.register_manager.unlockReg(addr_lock); |
| 7943 | |
| 7944 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 7945 | const atom_index = try elf_file.getOrCreateAtomForLazySymbol( |
| 7946 | .{ .kind = .const_data, .ty = Type.anyerror }, |
| 7947 | 4, // dword alignment |
| 7948 | ); |
| 7949 | const got_addr = elf_file.getAtom(atom_index).getOffsetTableAddress(elf_file); |
| 7950 | try self.asmRegisterMemory(.mov, addr_reg.to64(), Memory.sib(.qword, .{ |
| 7951 | .base = .ds, |
| 7952 | .disp = @intCast(i32, got_addr), |
| 7953 | })); |
| 7954 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 7955 | const atom_index = try coff_file.getOrCreateAtomForLazySymbol( |
| 7956 | .{ .kind = .const_data, .ty = Type.anyerror }, |
| 7957 | 4, // dword alignment |
| 7958 | ); |
| 7959 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; |
| 7960 | try self.genSetReg(Type.usize, addr_reg, .{ .linker_load = .{ |
| 7961 | .type = .got, |
| 7962 | .sym_index = sym_index, |
| 7963 | } }); |
| 7964 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 7965 | const atom_index = try macho_file.getOrCreateAtomForLazySymbol( |
| 7966 | .{ .kind = .const_data, .ty = Type.anyerror }, |
| 7967 | 4, // dword alignment |
| 7968 | ); |
| 7969 | const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; |
| 7970 | try self.genSetReg(Type.usize, addr_reg, .{ .linker_load = .{ |
| 7971 | .type = .got, |
| 7972 | .sym_index = sym_index, |
| 7973 | } }); |
| 7974 | } else { |
| 7975 | return self.fail("TODO implement airErrorName for x86_64 {s}", .{@tagName(self.bin_file.tag)}); |
| 7976 | } |
| 7977 | |
| 7978 | const start_reg = try self.register_manager.allocReg(null, gp); |
| 7979 | const start_lock = self.register_manager.lockRegAssumeUnused(start_reg); |
| 7980 | defer self.register_manager.unlockReg(start_lock); |
| 7981 | |
| 7982 | const end_reg = try self.register_manager.allocReg(null, gp); |
| 7983 | const end_lock = self.register_manager.lockRegAssumeUnused(end_reg); |
| 7984 | defer self.register_manager.unlockReg(end_lock); |
| 7985 | |
| 7986 | try self.truncateRegister(err_ty, err_reg.to32()); |
| 7987 | |
| 7988 | try self.asmRegisterMemory(.mov, start_reg.to32(), Memory.sib(.dword, .{ |
| 7989 | .base = addr_reg.to64(), |
| 7990 | .scale_index = .{ .scale = 4, .index = err_reg.to64() }, |
| 7991 | .disp = 0, |
| 7992 | })); |
| 7993 | try self.asmRegisterMemory(.mov, end_reg.to32(), Memory.sib(.dword, .{ |
| 7994 | .base = addr_reg.to64(), |
| 7995 | .scale_index = .{ .scale = 4, .index = err_reg.to64() }, |
| 7996 | .disp = 4, |
| 7997 | })); |
| 7998 | try self.asmRegisterRegister(.sub, end_reg.to32(), start_reg.to32()); |
| 7999 | try self.asmRegisterMemory(.lea, start_reg.to64(), Memory.sib(.byte, .{ |
| 8000 | .base = addr_reg.to64(), |
| 8001 | .scale_index = .{ .scale = 1, .index = start_reg.to64() }, |
| 8002 | .disp = 0, |
| 8003 | })); |
| 8004 | try self.asmRegisterMemory(.lea, end_reg.to32(), Memory.sib(.byte, .{ |
| 8005 | .base = end_reg.to64(), |
| 8006 | .disp = -1, |
| 8007 | })); |
| 8008 | |
| 8009 | const dst_mcv = try self.allocRegOrMem(inst, false); |
| 8010 | try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{ |
| 8011 | .base = .rbp, |
| 8012 | .disp = 0 - dst_mcv.stack_offset, |
| 8013 | }), start_reg.to64()); |
| 8014 | try self.asmMemoryRegister(.mov, Memory.sib(.qword, .{ |
| 8015 | .base = .rbp, |
| 8016 | .disp = 8 - dst_mcv.stack_offset, |
| 8017 | }), end_reg.to64()); |
| 8018 | break :result dst_mcv; |
| 7816 | 8019 | }; |
| 7817 | 8020 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 7818 | 8021 | } |
| ... | ... | @@ -7853,19 +8056,88 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 7853 | 8056 | if (self.liveness.isUnused(inst)) break :res MCValue.dead; |
| 7854 | 8057 | switch (result_ty.zigTypeTag()) { |
| 7855 | 8058 | .Struct => { |
| 7856 | | if (result_ty.containerLayout() == .Packed) { |
| 7857 | | return self.fail("TODO airAggregateInit implement packed structs", .{}); |
| 7858 | | } |
| 7859 | 8059 | const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); |
| 7860 | | for (elements, 0..) |elem, elem_i| { |
| 7861 | | if (result_ty.structFieldValueComptime(elem_i) != null) continue; // comptime elem |
| 8060 | const dst_mcv = MCValue{ .stack_offset = stack_offset }; |
| 8061 | if (result_ty.containerLayout() == .Packed) { |
| 8062 | const struct_obj = result_ty.castTag(.@"struct").?.data; |
| 8063 | try self.genInlineMemset( |
| 8064 | dst_mcv, |
| 8065 | .{ .immediate = 0 }, |
| 8066 | .{ .immediate = abi_size }, |
| 8067 | .{}, |
| 8068 | ); |
| 8069 | for (elements, 0..) |elem, elem_i| { |
| 8070 | if (result_ty.structFieldValueComptime(elem_i) != null) continue; |
| 8071 | |
| 8072 | const elem_ty = result_ty.structFieldType(elem_i); |
| 8073 | const elem_bit_size = @intCast(u32, elem_ty.bitSize(self.target.*)); |
| 8074 | if (elem_bit_size > 64) { |
| 8075 | return self.fail("TODO airAggregateInit implement packed structs with large fields", .{}); |
| 8076 | } |
| 8077 | const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| 8078 | const elem_abi_bits = elem_abi_size * 8; |
| 8079 | const elem_off = struct_obj.packedFieldBitOffset(self.target.*, elem_i); |
| 8080 | const elem_byte_off = @intCast(i32, elem_off / elem_abi_bits * elem_abi_size); |
| 8081 | const elem_bit_off = elem_off % elem_abi_bits; |
| 8082 | const elem_mcv = try self.resolveInst(elem); |
| 8083 | const elem_lock = switch (elem_mcv) { |
| 8084 | .register => |reg| self.register_manager.lockReg(reg), |
| 8085 | .immediate => |imm| lock: { |
| 8086 | if (imm == 0) continue; |
| 8087 | break :lock null; |
| 8088 | }, |
| 8089 | else => null, |
| 8090 | }; |
| 8091 | defer if (elem_lock) |lock| self.register_manager.unlockReg(lock); |
| 8092 | const elem_reg = registerAlias( |
| 8093 | try self.copyToTmpRegister(elem_ty, elem_mcv), |
| 8094 | elem_abi_size, |
| 8095 | ); |
| 8096 | const elem_extra_bits = self.regExtraBits(elem_ty); |
| 8097 | if (elem_bit_off < elem_extra_bits) { |
| 8098 | try self.truncateRegister(elem_ty, elem_reg); |
| 8099 | } |
| 8100 | if (elem_bit_off > 0) try self.genShiftBinOpMir( |
| 8101 | .shl, |
| 8102 | elem_ty, |
| 8103 | .{ .register = elem_reg }, |
| 8104 | .{ .immediate = elem_bit_off }, |
| 8105 | ); |
| 8106 | try self.genBinOpMir( |
| 8107 | .@"or", |
| 8108 | elem_ty, |
| 8109 | .{ .stack_offset = stack_offset - elem_byte_off }, |
| 8110 | .{ .register = elem_reg }, |
| 8111 | ); |
| 8112 | if (elem_bit_off > elem_extra_bits) { |
| 8113 | const reg = try self.copyToTmpRegister(elem_ty, elem_mcv); |
| 8114 | if (elem_extra_bits > 0) { |
| 8115 | try self.truncateRegister(elem_ty, registerAlias(reg, elem_abi_size)); |
| 8116 | } |
| 8117 | try self.genShiftBinOpMir( |
| 8118 | .shr, |
| 8119 | elem_ty, |
| 8120 | .{ .register = reg }, |
| 8121 | .{ .immediate = elem_abi_bits - elem_bit_off }, |
| 8122 | ); |
| 8123 | try self.genBinOpMir( |
| 8124 | .@"or", |
| 8125 | elem_ty, |
| 8126 | .{ .stack_offset = stack_offset - elem_byte_off - |
| 8127 | @intCast(i32, elem_abi_size) }, |
| 8128 | .{ .register = reg }, |
| 8129 | ); |
| 8130 | } |
| 8131 | } |
| 8132 | } else for (elements, 0..) |elem, elem_i| { |
| 8133 | if (result_ty.structFieldValueComptime(elem_i) != null) continue; |
| 7862 | 8134 | |
| 7863 | 8135 | const elem_ty = result_ty.structFieldType(elem_i); |
| 7864 | | const elem_off = result_ty.structFieldOffset(elem_i, self.target.*); |
| 8136 | const elem_off = @intCast(i32, result_ty.structFieldOffset(elem_i, self.target.*)); |
| 7865 | 8137 | const elem_mcv = try self.resolveInst(elem); |
| 7866 | | try self.genSetStack(elem_ty, stack_offset - @intCast(i32, elem_off), elem_mcv, .{}); |
| 8138 | try self.genSetStack(elem_ty, stack_offset - elem_off, elem_mcv, .{}); |
| 7867 | 8139 | } |
| 7868 | | break :res MCValue{ .stack_offset = stack_offset }; |
| 8140 | break :res dst_mcv; |
| 7869 | 8141 | }, |
| 7870 | 8142 | .Array => { |
| 7871 | 8143 | const stack_offset = @intCast(i32, try self.allocMem(inst, abi_size, abi_align)); |
| ... | ... | @@ -7980,7 +8252,7 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV |
| 7980 | 8252 | // This immediate is unsigned. |
| 7981 | 8253 | const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed)); |
| 7982 | 8254 | if (imm >= math.maxInt(U)) { |
| 7983 | | return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) }; |
| 8255 | return MCValue{ .register = try self.copyToTmpRegister(Type.usize, mcv) }; |
| 7984 | 8256 | } |
| 7985 | 8257 | }, |
| 7986 | 8258 | else => {}, |
| ... | ... | @@ -8023,11 +8295,18 @@ const CallMCValues = struct { |
| 8023 | 8295 | }; |
| 8024 | 8296 | |
| 8025 | 8297 | /// Caller must call `CallMCValues.deinit`. |
| 8026 | | fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 8298 | fn resolveCallingConventionValues( |
| 8299 | self: *Self, |
| 8300 | fn_ty: Type, |
| 8301 | var_args: []const Air.Inst.Ref, |
| 8302 | ) !CallMCValues { |
| 8027 | 8303 | const cc = fn_ty.fnCallingConvention(); |
| 8028 | | const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); |
| 8304 | const param_len = fn_ty.fnParamLen(); |
| 8305 | const param_types = try self.gpa.alloc(Type, param_len + var_args.len); |
| 8029 | 8306 | defer self.gpa.free(param_types); |
| 8030 | 8307 | fn_ty.fnParamTypes(param_types); |
| 8308 | // TODO: promote var arg types |
| 8309 | for (param_types[param_len..], var_args) |*param_ty, arg| param_ty.* = self.air.typeOf(arg); |
| 8031 | 8310 | var result: CallMCValues = .{ |
| 8032 | 8311 | .args = try self.gpa.alloc(MCValue, param_types.len), |
| 8033 | 8312 | // These undefined values must be populated before returning from this function. |
| ... | ... | @@ -8248,8 +8527,8 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { |
| 8248 | 8527 | .unsigned => { |
| 8249 | 8528 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); |
| 8250 | 8529 | const mask = (~@as(u64, 0)) >> shift; |
| 8251 | | if (int_info.bits < 32) { |
| 8252 | | try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .immediate = mask }); |
| 8530 | if (int_info.bits <= 32) { |
| 8531 | try self.genBinOpMir(.@"and", Type.u32, .{ .register = reg }, .{ .immediate = mask }); |
| 8253 | 8532 | } else { |
| 8254 | 8533 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); |
| 8255 | 8534 | try self.genBinOpMir(.@"and", Type.usize, .{ .register = reg }, .{ .register = tmp_reg }); |