| ... | ... | @@ -1497,7 +1497,7 @@ fn allocRegs( |
| 1497 | 1497 | /// instructions which are binary operations acting on two registers |
| 1498 | 1498 | /// |
| 1499 | 1499 | /// Returns the destination register |
| 1500 | | fn binOpRegisterNew( |
| 1500 | fn binOpRegister( |
| 1501 | 1501 | self: *Self, |
| 1502 | 1502 | mir_tag: Mir.Inst.Tag, |
| 1503 | 1503 | lhs_bind: ReadArg.Bind, |
| ... | ... | @@ -1582,7 +1582,7 @@ fn binOpRegisterNew( |
| 1582 | 1582 | /// an immediate |
| 1583 | 1583 | /// |
| 1584 | 1584 | /// Returns the destination register |
| 1585 | | fn binOpImmediateNew( |
| 1585 | fn binOpImmediate( |
| 1586 | 1586 | self: *Self, |
| 1587 | 1587 | mir_tag: Mir.Inst.Tag, |
| 1588 | 1588 | lhs_bind: ReadArg.Bind, |
| ... | ... | @@ -1639,258 +1639,6 @@ fn binOpImmediateNew( |
| 1639 | 1639 | return MCValue{ .register = dest_reg }; |
| 1640 | 1640 | } |
| 1641 | 1641 | |
| 1642 | | /// Don't call this function directly. Use binOp instead. |
| 1643 | | /// |
| 1644 | | /// Calling this function signals an intention to generate a Mir |
| 1645 | | /// instruction of the form |
| 1646 | | /// |
| 1647 | | /// op dest, lhs, rhs |
| 1648 | | /// |
| 1649 | | /// Asserts that generating an instruction of that form is possible. |
| 1650 | | fn binOpRegister( |
| 1651 | | self: *Self, |
| 1652 | | mir_tag: Mir.Inst.Tag, |
| 1653 | | lhs: MCValue, |
| 1654 | | rhs: MCValue, |
| 1655 | | lhs_ty: Type, |
| 1656 | | rhs_ty: Type, |
| 1657 | | metadata: ?BinOpMetadata, |
| 1658 | | ) !MCValue { |
| 1659 | | const lhs_is_register = lhs == .register; |
| 1660 | | const rhs_is_register = rhs == .register; |
| 1661 | | |
| 1662 | | if (lhs_is_register) assert(lhs.register == self.registerAlias(lhs.register, lhs_ty)); |
| 1663 | | if (rhs_is_register) assert(rhs.register == self.registerAlias(rhs.register, rhs_ty)); |
| 1664 | | |
| 1665 | | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| 1666 | | self.register_manager.lockReg(lhs.register) |
| 1667 | | else |
| 1668 | | null; |
| 1669 | | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1670 | | |
| 1671 | | const rhs_lock: ?RegisterLock = if (rhs_is_register) |
| 1672 | | self.register_manager.lockReg(rhs.register) |
| 1673 | | else |
| 1674 | | null; |
| 1675 | | defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1676 | | |
| 1677 | | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1678 | | |
| 1679 | | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 1680 | | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 1681 | | break :inst Air.refToIndex(md.lhs).?; |
| 1682 | | } else null; |
| 1683 | | |
| 1684 | | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1685 | | const reg = self.registerAlias(raw_reg, lhs_ty); |
| 1686 | | |
| 1687 | | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1688 | | |
| 1689 | | break :blk reg; |
| 1690 | | }; |
| 1691 | | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 1692 | | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1693 | | |
| 1694 | | const rhs_reg = if (rhs_is_register) |
| 1695 | | // lhs is almost always equal to rhs, except in shifts. In |
| 1696 | | // order to guarantee that registers will have equal sizes, we |
| 1697 | | // use the register alias of rhs corresponding to the size of |
| 1698 | | // lhs. |
| 1699 | | self.registerAlias(rhs.register, lhs_ty) |
| 1700 | | else blk: { |
| 1701 | | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 1702 | | break :inst Air.refToIndex(md.rhs).?; |
| 1703 | | } else null; |
| 1704 | | |
| 1705 | | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1706 | | |
| 1707 | | // Here, we deliberately use lhs as lhs and rhs may differ in |
| 1708 | | // the case of shifts. See comment above. |
| 1709 | | const reg = self.registerAlias(raw_reg, lhs_ty); |
| 1710 | | |
| 1711 | | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1712 | | |
| 1713 | | break :blk reg; |
| 1714 | | }; |
| 1715 | | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); |
| 1716 | | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1717 | | |
| 1718 | | const dest_reg = switch (mir_tag) { |
| 1719 | | else => if (metadata) |md| blk: { |
| 1720 | | if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { |
| 1721 | | break :blk lhs_reg; |
| 1722 | | } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { |
| 1723 | | break :blk rhs_reg; |
| 1724 | | } else { |
| 1725 | | const raw_reg = try self.register_manager.allocReg(md.inst, gp); |
| 1726 | | break :blk self.registerAlias(raw_reg, lhs_ty); |
| 1727 | | } |
| 1728 | | } else blk: { |
| 1729 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1730 | | break :blk self.registerAlias(raw_reg, lhs_ty); |
| 1731 | | }, |
| 1732 | | }; |
| 1733 | | |
| 1734 | | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1735 | | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 1736 | | |
| 1737 | | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1738 | | .add_shifted_register, |
| 1739 | | .adds_shifted_register, |
| 1740 | | .sub_shifted_register, |
| 1741 | | .subs_shifted_register, |
| 1742 | | => .{ .rrr_imm6_shift = .{ |
| 1743 | | .rd = dest_reg, |
| 1744 | | .rn = lhs_reg, |
| 1745 | | .rm = rhs_reg, |
| 1746 | | .imm6 = 0, |
| 1747 | | .shift = .lsl, |
| 1748 | | } }, |
| 1749 | | .mul, |
| 1750 | | .lsl_register, |
| 1751 | | .asr_register, |
| 1752 | | .lsr_register, |
| 1753 | | .sdiv, |
| 1754 | | .udiv, |
| 1755 | | => .{ .rrr = .{ |
| 1756 | | .rd = dest_reg, |
| 1757 | | .rn = lhs_reg, |
| 1758 | | .rm = rhs_reg, |
| 1759 | | } }, |
| 1760 | | .smull, |
| 1761 | | .umull, |
| 1762 | | => .{ .rrr = .{ |
| 1763 | | .rd = dest_reg.toX(), |
| 1764 | | .rn = lhs_reg, |
| 1765 | | .rm = rhs_reg, |
| 1766 | | } }, |
| 1767 | | .and_shifted_register, |
| 1768 | | .orr_shifted_register, |
| 1769 | | .eor_shifted_register, |
| 1770 | | => .{ .rrr_imm6_logical_shift = .{ |
| 1771 | | .rd = dest_reg, |
| 1772 | | .rn = lhs_reg, |
| 1773 | | .rm = rhs_reg, |
| 1774 | | .imm6 = 0, |
| 1775 | | .shift = .lsl, |
| 1776 | | } }, |
| 1777 | | else => unreachable, |
| 1778 | | }; |
| 1779 | | |
| 1780 | | _ = try self.addInst(.{ |
| 1781 | | .tag = mir_tag, |
| 1782 | | .data = mir_data, |
| 1783 | | }); |
| 1784 | | |
| 1785 | | return MCValue{ .register = dest_reg }; |
| 1786 | | } |
| 1787 | | |
| 1788 | | /// Don't call this function directly. Use binOp instead. |
| 1789 | | /// |
| 1790 | | /// Calling this function signals an intention to generate a Mir |
| 1791 | | /// instruction of the form |
| 1792 | | /// |
| 1793 | | /// op dest, lhs, #rhs_imm |
| 1794 | | /// |
| 1795 | | /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to |
| 1796 | | /// rhs and vice versa. This parameter is only used when maybe_inst != |
| 1797 | | /// null. |
| 1798 | | /// |
| 1799 | | /// Asserts that generating an instruction of that form is possible. |
| 1800 | | fn binOpImmediate( |
| 1801 | | self: *Self, |
| 1802 | | mir_tag: Mir.Inst.Tag, |
| 1803 | | lhs: MCValue, |
| 1804 | | rhs: MCValue, |
| 1805 | | lhs_ty: Type, |
| 1806 | | lhs_and_rhs_swapped: bool, |
| 1807 | | metadata: ?BinOpMetadata, |
| 1808 | | ) !MCValue { |
| 1809 | | const lhs_is_register = lhs == .register; |
| 1810 | | |
| 1811 | | if (lhs_is_register) assert(lhs.register == self.registerAlias(lhs.register, lhs_ty)); |
| 1812 | | |
| 1813 | | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| 1814 | | self.register_manager.lockReg(lhs.register) |
| 1815 | | else |
| 1816 | | null; |
| 1817 | | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1818 | | |
| 1819 | | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1820 | | |
| 1821 | | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 1822 | | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 1823 | | break :inst Air.refToIndex( |
| 1824 | | if (lhs_and_rhs_swapped) md.rhs else md.lhs, |
| 1825 | | ).?; |
| 1826 | | } else null; |
| 1827 | | |
| 1828 | | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1829 | | const reg = self.registerAlias(raw_reg, lhs_ty); |
| 1830 | | |
| 1831 | | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1832 | | |
| 1833 | | break :blk reg; |
| 1834 | | }; |
| 1835 | | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 1836 | | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1837 | | |
| 1838 | | const dest_reg = switch (mir_tag) { |
| 1839 | | else => if (metadata) |md| blk: { |
| 1840 | | if (lhs_is_register and self.reuseOperand( |
| 1841 | | md.inst, |
| 1842 | | if (lhs_and_rhs_swapped) md.rhs else md.lhs, |
| 1843 | | if (lhs_and_rhs_swapped) 1 else 0, |
| 1844 | | lhs, |
| 1845 | | )) { |
| 1846 | | break :blk lhs_reg; |
| 1847 | | } else { |
| 1848 | | const raw_reg = try self.register_manager.allocReg(md.inst, gp); |
| 1849 | | break :blk self.registerAlias(raw_reg, lhs_ty); |
| 1850 | | } |
| 1851 | | } else blk: { |
| 1852 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1853 | | break :blk self.registerAlias(raw_reg, lhs_ty); |
| 1854 | | }, |
| 1855 | | }; |
| 1856 | | |
| 1857 | | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1858 | | |
| 1859 | | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1860 | | .add_immediate, |
| 1861 | | .adds_immediate, |
| 1862 | | .sub_immediate, |
| 1863 | | .subs_immediate, |
| 1864 | | => .{ .rr_imm12_sh = .{ |
| 1865 | | .rd = dest_reg, |
| 1866 | | .rn = lhs_reg, |
| 1867 | | .imm12 = @intCast(u12, rhs.immediate), |
| 1868 | | } }, |
| 1869 | | .lsl_immediate, |
| 1870 | | .asr_immediate, |
| 1871 | | .lsr_immediate, |
| 1872 | | => .{ .rr_shift = .{ |
| 1873 | | .rd = dest_reg, |
| 1874 | | .rn = lhs_reg, |
| 1875 | | .shift = @intCast(u6, rhs.immediate), |
| 1876 | | } }, |
| 1877 | | else => unreachable, |
| 1878 | | }; |
| 1879 | | |
| 1880 | | _ = try self.addInst(.{ |
| 1881 | | .tag = mir_tag, |
| 1882 | | .data = mir_data, |
| 1883 | | }); |
| 1884 | | |
| 1885 | | return MCValue{ .register = dest_reg }; |
| 1886 | | } |
| 1887 | | |
| 1888 | | const BinOpMetadata = struct { |
| 1889 | | inst: Air.Inst.Index, |
| 1890 | | lhs: Air.Inst.Ref, |
| 1891 | | rhs: Air.Inst.Ref, |
| 1892 | | }; |
| 1893 | | |
| 1894 | 1642 | fn addSub( |
| 1895 | 1643 | self: *Self, |
| 1896 | 1644 | tag: Air.Inst.Tag, |
| ... | ... | @@ -1938,12 +1686,12 @@ fn addSub( |
| 1938 | 1686 | }; |
| 1939 | 1687 | |
| 1940 | 1688 | if (rhs_immediate_ok) { |
| 1941 | | return try self.binOpImmediateNew(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); |
| 1689 | return try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); |
| 1942 | 1690 | } else if (lhs_immediate_ok) { |
| 1943 | 1691 | // swap lhs and rhs |
| 1944 | | return try self.binOpImmediateNew(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); |
| 1692 | return try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); |
| 1945 | 1693 | } else { |
| 1946 | | return try self.binOpRegisterNew(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1694 | return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1947 | 1695 | } |
| 1948 | 1696 | } else { |
| 1949 | 1697 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| ... | ... | @@ -1971,7 +1719,7 @@ fn mul( |
| 1971 | 1719 | // TODO add optimisations for multiplication |
| 1972 | 1720 | // with immediates, for example a * 2 can be |
| 1973 | 1721 | // lowered to a << 1 |
| 1974 | | return try self.binOpRegisterNew(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1722 | return try self.binOpRegister(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1975 | 1723 | } else { |
| 1976 | 1724 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1977 | 1725 | } |
| ... | ... | @@ -2019,11 +1767,11 @@ fn divTrunc( |
| 2019 | 1767 | switch (int_info.signedness) { |
| 2020 | 1768 | .signed => { |
| 2021 | 1769 | // TODO optimize integer division by constants |
| 2022 | | return try self.binOpRegisterNew(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1770 | return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2023 | 1771 | }, |
| 2024 | 1772 | .unsigned => { |
| 2025 | 1773 | // TODO optimize integer division by constants |
| 2026 | | return try self.binOpRegisterNew(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1774 | return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2027 | 1775 | }, |
| 2028 | 1776 | } |
| 2029 | 1777 | } else { |
| ... | ... | @@ -2056,7 +1804,7 @@ fn divFloor( |
| 2056 | 1804 | }, |
| 2057 | 1805 | .unsigned => { |
| 2058 | 1806 | // TODO optimize integer division by constants |
| 2059 | | return try self.binOpRegisterNew(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1807 | return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2060 | 1808 | }, |
| 2061 | 1809 | } |
| 2062 | 1810 | } else { |
| ... | ... | @@ -2086,11 +1834,11 @@ fn divExact( |
| 2086 | 1834 | switch (int_info.signedness) { |
| 2087 | 1835 | .signed => { |
| 2088 | 1836 | // TODO optimize integer division by constants |
| 2089 | | return try self.binOpRegisterNew(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1837 | return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2090 | 1838 | }, |
| 2091 | 1839 | .unsigned => { |
| 2092 | 1840 | // TODO optimize integer division by constants |
| 2093 | | return try self.binOpRegisterNew(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1841 | return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2094 | 1842 | }, |
| 2095 | 1843 | } |
| 2096 | 1844 | } else { |
| ... | ... | @@ -2248,7 +1996,7 @@ fn bitwise( |
| 2248 | 1996 | else => unreachable, |
| 2249 | 1997 | }; |
| 2250 | 1998 | |
| 2251 | | return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1999 | return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2252 | 2000 | } else { |
| 2253 | 2001 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 2254 | 2002 | } |
| ... | ... | @@ -2293,12 +2041,12 @@ fn shiftExact( |
| 2293 | 2041 | }; |
| 2294 | 2042 | |
| 2295 | 2043 | if (rhs_immediate) |imm| { |
| 2296 | | return try self.binOpImmediateNew(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst); |
| 2044 | return try self.binOpImmediate(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst); |
| 2297 | 2045 | } else { |
| 2298 | 2046 | // We intentionally pass lhs_ty here in order to |
| 2299 | 2047 | // prevent using the 32-bit register alias when |
| 2300 | 2048 | // lhs_ty is > 32 bits. |
| 2301 | | return try self.binOpRegisterNew(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst); |
| 2049 | return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst); |
| 2302 | 2050 | } |
| 2303 | 2051 | } else { |
| 2304 | 2052 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| ... | ... | @@ -2367,7 +2115,7 @@ fn booleanOp( |
| 2367 | 2115 | else => unreachable, |
| 2368 | 2116 | }; |
| 2369 | 2117 | |
| 2370 | | return try self.binOpRegisterNew(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2118 | return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2371 | 2119 | }, |
| 2372 | 2120 | else => unreachable, |
| 2373 | 2121 | } |
| ... | ... | @@ -2598,12 +2346,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2598 | 2346 | |
| 2599 | 2347 | const dest = blk: { |
| 2600 | 2348 | if (rhs_immediate_ok) { |
| 2601 | | break :blk try self.binOpImmediateNew(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, null); |
| 2349 | break :blk try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, null); |
| 2602 | 2350 | } else if (lhs_immediate_ok) { |
| 2603 | 2351 | // swap lhs and rhs |
| 2604 | | break :blk try self.binOpImmediateNew(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, null); |
| 2352 | break :blk try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, null); |
| 2605 | 2353 | } else { |
| 2606 | | break :blk try self.binOpRegisterNew(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); |
| 2354 | break :blk try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); |
| 2607 | 2355 | } |
| 2608 | 2356 | }; |
| 2609 | 2357 | |
| ... | ... | @@ -2634,8 +2382,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2634 | 2382 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2635 | 2383 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 2636 | 2384 | const result: MCValue = result: { |
| 2637 | | const lhs = try self.resolveInst(extra.lhs); |
| 2638 | | const rhs = try self.resolveInst(extra.rhs); |
| 2385 | const mod = self.bin_file.options.module.?; |
| 2386 | |
| 2387 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 2388 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2639 | 2389 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2640 | 2390 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 2641 | 2391 | |
| ... | ... | @@ -2647,20 +2397,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2647 | 2397 | switch (lhs_ty.zigTypeTag()) { |
| 2648 | 2398 | .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), |
| 2649 | 2399 | .Int => { |
| 2400 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 2650 | 2401 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2651 | | |
| 2652 | 2402 | if (int_info.bits <= 32) { |
| 2653 | 2403 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); |
| 2654 | 2404 | |
| 2655 | 2405 | try self.spillCompareFlagsIfOccupied(); |
| 2656 | | self.condition_flags_inst = null; |
| 2657 | 2406 | |
| 2658 | 2407 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { |
| 2659 | 2408 | .signed => .smull, |
| 2660 | 2409 | .unsigned => .umull, |
| 2661 | 2410 | }; |
| 2662 | 2411 | |
| 2663 | | const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); |
| 2412 | const dest = try self.binOpRegister(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); |
| 2664 | 2413 | const dest_reg = dest.register; |
| 2665 | 2414 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2666 | 2415 | defer self.register_manager.unlockReg(dest_reg_lock); |
| ... | ... | @@ -2709,50 +2458,27 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2709 | 2458 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); |
| 2710 | 2459 | |
| 2711 | 2460 | try self.spillCompareFlagsIfOccupied(); |
| 2712 | | self.condition_flags_inst = null; |
| 2713 | | |
| 2714 | | // TODO this should really be put in a helper similar to `binOpRegister` |
| 2715 | | const lhs_is_register = lhs == .register; |
| 2716 | | const rhs_is_register = rhs == .register; |
| 2717 | | |
| 2718 | | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| 2719 | | self.register_manager.lockRegAssumeUnused(lhs.register) |
| 2720 | | else |
| 2721 | | null; |
| 2722 | | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2723 | | |
| 2724 | | const rhs_lock: ?RegisterLock = if (rhs_is_register) |
| 2725 | | self.register_manager.lockRegAssumeUnused(rhs.register) |
| 2726 | | else |
| 2727 | | null; |
| 2728 | | defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2729 | | |
| 2730 | | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 2731 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 2732 | | const reg = self.registerAlias(raw_reg, lhs_ty); |
| 2733 | | break :blk reg; |
| 2734 | | }; |
| 2735 | | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 2736 | | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2737 | 2461 | |
| 2738 | | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 2739 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 2740 | | const reg = self.registerAlias(raw_reg, rhs_ty); |
| 2741 | | break :blk reg; |
| 2742 | | }; |
| 2743 | | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); |
| 2744 | | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2745 | | |
| 2746 | | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2747 | | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2462 | var lhs_reg: Register = undefined; |
| 2463 | var rhs_reg: Register = undefined; |
| 2464 | var dest_reg: Register = undefined; |
| 2465 | var dest_high_reg: Register = undefined; |
| 2466 | var truncated_reg: Register = undefined; |
| 2748 | 2467 | |
| 2749 | | const dest_reg = blk: { |
| 2750 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 2751 | | const reg = self.registerAlias(raw_reg, lhs_ty); |
| 2752 | | break :blk reg; |
| 2468 | const read_args = [_]ReadArg{ |
| 2469 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 2470 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, |
| 2753 | 2471 | }; |
| 2754 | | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2755 | | defer self.register_manager.unlockReg(dest_reg_lock); |
| 2472 | const write_args = [_]WriteArg{ |
| 2473 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 2474 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_high_reg }, |
| 2475 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg }, |
| 2476 | }; |
| 2477 | try self.allocRegs( |
| 2478 | &read_args, |
| 2479 | &write_args, |
| 2480 | null, |
| 2481 | ); |
| 2756 | 2482 | |
| 2757 | 2483 | switch (int_info.signedness) { |
| 2758 | 2484 | .signed => { |
| ... | ... | @@ -2766,10 +2492,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2766 | 2492 | } }, |
| 2767 | 2493 | }); |
| 2768 | 2494 | |
| 2769 | | const dest_high_reg = try self.register_manager.allocReg(null, gp); |
| 2770 | | const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); |
| 2771 | | defer self.register_manager.unlockReg(dest_high_reg_lock); |
| 2772 | | |
| 2773 | 2495 | // smulh dest_high, lhs, rhs |
| 2774 | 2496 | _ = try self.addInst(.{ |
| 2775 | 2497 | .tag = .smulh, |
| ... | ... | @@ -2816,10 +2538,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2816 | 2538 | } |
| 2817 | 2539 | }, |
| 2818 | 2540 | .unsigned => { |
| 2819 | | const dest_high_reg = try self.register_manager.allocReg(null, gp); |
| 2820 | | const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); |
| 2821 | | defer self.register_manager.unlockReg(dest_high_reg_lock); |
| 2822 | | |
| 2823 | 2541 | // umulh dest_high, lhs, rhs |
| 2824 | 2542 | _ = try self.addInst(.{ |
| 2825 | 2543 | .tag = .umulh, |
| ... | ... | @@ -2870,10 +2588,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2870 | 2588 | }, |
| 2871 | 2589 | } |
| 2872 | 2590 | |
| 2873 | | const truncated_reg = try self.register_manager.allocReg(null, gp); |
| 2874 | | const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); |
| 2875 | | defer self.register_manager.unlockReg(truncated_reg_lock); |
| 2876 | | |
| 2877 | 2591 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 2878 | 2592 | |
| 2879 | 2593 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| ... | ... | @@ -2893,6 +2607,8 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2893 | 2607 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2894 | 2608 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 2895 | 2609 | const result: MCValue = result: { |
| 2610 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 2611 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2896 | 2612 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2897 | 2613 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 2898 | 2614 | |
| ... | ... | @@ -2910,9 +2626,6 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2910 | 2626 | |
| 2911 | 2627 | try self.spillCompareFlagsIfOccupied(); |
| 2912 | 2628 | |
| 2913 | | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 2914 | | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2915 | | |
| 2916 | 2629 | var lhs_reg: Register = undefined; |
| 2917 | 2630 | var rhs_reg: Register = undefined; |
| 2918 | 2631 | var dest_reg: Register = undefined; |