| ... | ... | @@ -38,23 +38,9 @@ const Memory = bits.Memory; |
| 38 | 38 | const FrameIndex = bits.FrameIndex; |
| 39 | 39 | const RegisterManager = abi.RegisterManager; |
| 40 | 40 | const RegisterLock = RegisterManager.RegisterLock; |
| 41 | | const callee_preserved_regs = abi.callee_preserved_regs; |
| 42 | | /// General Purpose |
| 43 | | const gp = abi.RegisterClass.gp; |
| 44 | | /// Function Args |
| 45 | | const fa = abi.RegisterClass.fa; |
| 46 | | /// Function Returns |
| 47 | | const fr = abi.RegisterClass.fr; |
| 48 | | /// Temporary Use |
| 49 | | const tp = abi.RegisterClass.tp; |
| 50 | 41 | |
| 51 | 42 | const InnerError = CodeGenError || error{OutOfRegisters}; |
| 52 | 43 | |
| 53 | | const RegisterView = enum(u1) { |
| 54 | | caller, |
| 55 | | callee, |
| 56 | | }; |
| 57 | | |
| 58 | 44 | gpa: Allocator, |
| 59 | 45 | air: Air, |
| 60 | 46 | mod: *Package.Module, |
| ... | ... | @@ -919,10 +905,24 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { |
| 919 | 905 | return result; |
| 920 | 906 | } |
| 921 | 907 | |
| 908 | const required_features = [_]Target.riscv.Feature{ |
| 909 | .d, |
| 910 | .m, |
| 911 | }; |
| 912 | |
| 922 | 913 | fn gen(self: *Self) !void { |
| 923 | 914 | const mod = self.bin_file.comp.module.?; |
| 924 | 915 | const fn_info = mod.typeToFunc(self.fn_type).?; |
| 925 | 916 | |
| 917 | inline for (required_features) |feature| { |
| 918 | if (!self.hasFeature(feature)) { |
| 919 | return self.fail( |
| 920 | "target missing required feature {s}", |
| 921 | .{@tagName(feature)}, |
| 922 | ); |
| 923 | } |
| 924 | } |
| 925 | |
| 926 | 926 | if (fn_info.cc != .Naked) { |
| 927 | 927 | try self.addPseudoNone(.pseudo_dbg_prologue_end); |
| 928 | 928 | |
| ... | ... | @@ -1454,9 +1454,9 @@ fn computeFrameLayout(self: *Self) !FrameLayout { |
| 1454 | 1454 | } |
| 1455 | 1455 | |
| 1456 | 1456 | var save_reg_list = Mir.RegisterList{}; |
| 1457 | | for (callee_preserved_regs) |reg| { |
| 1457 | for (abi.Registers.all_preserved) |reg| { |
| 1458 | 1458 | if (self.register_manager.isRegAllocated(reg)) { |
| 1459 | | save_reg_list.push(&callee_preserved_regs, reg); |
| 1459 | save_reg_list.push(&abi.Registers.all_preserved, reg); |
| 1460 | 1460 | } |
| 1461 | 1461 | } |
| 1462 | 1462 | |
| ... | ... | @@ -1600,6 +1600,33 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !FrameIndex { |
| 1600 | 1600 | })); |
| 1601 | 1601 | } |
| 1602 | 1602 | |
| 1603 | fn typeRegClass(self: *Self, ty: Type) abi.RegisterClass { |
| 1604 | const zcu = self.bin_file.comp.module.?; |
| 1605 | return switch (ty.zigTypeTag(zcu)) { |
| 1606 | .Float => .float, |
| 1607 | .Vector => @panic("TODO: typeRegClass for Vectors"), |
| 1608 | inline else => .int, |
| 1609 | }; |
| 1610 | } |
| 1611 | |
| 1612 | fn regGeneralClassForType(self: *Self, ty: Type) RegisterManager.RegisterBitSet { |
| 1613 | const zcu = self.bin_file.comp.module.?; |
| 1614 | return switch (ty.zigTypeTag(zcu)) { |
| 1615 | .Float => abi.Registers.Float.general_purpose, |
| 1616 | .Vector => @panic("TODO: regGeneralClassForType for Vectors"), |
| 1617 | else => abi.Registers.Integer.general_purpose, |
| 1618 | }; |
| 1619 | } |
| 1620 | |
| 1621 | fn regTempClassForType(self: *Self, ty: Type) RegisterManager.RegisterBitSet { |
| 1622 | const zcu = self.bin_file.comp.module.?; |
| 1623 | return switch (ty.zigTypeTag(zcu)) { |
| 1624 | .Float => abi.Registers.Float.temporary, |
| 1625 | .Vector => @panic("TODO: regTempClassForType for Vectors"), |
| 1626 | else => abi.Registers.Integer.temporary, |
| 1627 | }; |
| 1628 | } |
| 1629 | |
| 1603 | 1630 | fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1604 | 1631 | const zcu = self.bin_file.comp.module.?; |
| 1605 | 1632 | const elem_ty = self.typeOfIndex(inst); |
| ... | ... | @@ -1608,11 +1635,15 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1608 | 1635 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(zcu)}); |
| 1609 | 1636 | }; |
| 1610 | 1637 | |
| 1611 | | if (reg_ok) { |
| 1612 | | if (abi_size <= 8) { |
| 1613 | | if (self.register_manager.tryAllocReg(inst, gp)) |reg| { |
| 1614 | | return .{ .register = reg }; |
| 1615 | | } |
| 1638 | const min_size: u32 = switch (elem_ty.zigTypeTag(zcu)) { |
| 1639 | .Float => 4, |
| 1640 | .Vector => @panic("allocRegOrMem Vector"), |
| 1641 | else => 8, |
| 1642 | }; |
| 1643 | |
| 1644 | if (reg_ok and abi_size <= min_size) { |
| 1645 | if (self.register_manager.tryAllocReg(inst, self.regGeneralClassForType(elem_ty))) |reg| { |
| 1646 | return .{ .register = reg }; |
| 1616 | 1647 | } |
| 1617 | 1648 | } |
| 1618 | 1649 | |
| ... | ... | @@ -1623,19 +1654,37 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1623 | 1654 | /// Allocates a register from the general purpose set and returns the Register and the Lock. |
| 1624 | 1655 | /// |
| 1625 | 1656 | /// Up to the caller to unlock the register later. |
| 1626 | | fn allocReg(self: *Self) !struct { Register, RegisterLock } { |
| 1627 | | const reg = try self.register_manager.allocReg(null, gp); |
| 1657 | fn allocReg(self: *Self, reg_class: abi.RegisterClass) !struct { Register, RegisterLock } { |
| 1658 | if (reg_class == .float and !self.hasFeature(.f)) |
| 1659 | std.debug.panic("allocReg class == float where F isn't enabled", .{}); |
| 1660 | |
| 1661 | const class = switch (reg_class) { |
| 1662 | .int => abi.Registers.Integer.general_purpose, |
| 1663 | .float => abi.Registers.Float.general_purpose, |
| 1664 | }; |
| 1665 | |
| 1666 | const reg = try self.register_manager.allocReg(null, class); |
| 1628 | 1667 | const lock = self.register_manager.lockRegAssumeUnused(reg); |
| 1629 | 1668 | return .{ reg, lock }; |
| 1630 | 1669 | } |
| 1631 | 1670 | |
| 1671 | /// Similar to `allocReg` but will copy the MCValue into the Register unless `operand` is already |
| 1672 | /// a register, in which case it will return a possible lock to that register. |
| 1673 | fn promoteReg(self: *Self, ty: Type, operand: MCValue) !struct { Register, ?RegisterLock } { |
| 1674 | if (operand == .register) return .{ operand.register, self.register_manager.lockReg(operand.register) }; |
| 1675 | |
| 1676 | const reg, const lock = try self.allocReg(self.typeRegClass(ty)); |
| 1677 | try self.genSetReg(ty, reg, operand); |
| 1678 | return .{ reg, lock }; |
| 1679 | } |
| 1680 | |
| 1632 | 1681 | fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Register { |
| 1633 | 1682 | const reg: Register = blk: { |
| 1634 | 1683 | switch (index) { |
| 1635 | 1684 | .immediate => |imm| { |
| 1636 | 1685 | // Optimisation: if index MCValue is an immediate, we can multiply in `comptime` |
| 1637 | 1686 | // and set the register directly to the scaled offset as an immediate. |
| 1638 | | const reg = try self.register_manager.allocReg(null, gp); |
| 1687 | const reg = try self.register_manager.allocReg(null, self.regGeneralClassForType(index_ty)); |
| 1639 | 1688 | try self.genSetReg(index_ty, reg, .{ .immediate = imm * elem_size }); |
| 1640 | 1689 | break :blk reg; |
| 1641 | 1690 | }, |
| ... | ... | @@ -1671,7 +1720,8 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 1671 | 1720 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 1672 | 1721 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 1673 | 1722 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 1674 | | const reg = try self.register_manager.allocReg(null, tp); |
| 1723 | log.debug("copyToTmpRegister ty: {}", .{ty.fmt(self.bin_file.comp.module.?)}); |
| 1724 | const reg = try self.register_manager.allocReg(null, self.regTempClassForType(ty)); |
| 1675 | 1725 | try self.genSetReg(ty, reg, mcv); |
| 1676 | 1726 | return reg; |
| 1677 | 1727 | } |
| ... | ... | @@ -1680,7 +1730,8 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 1680 | 1730 | /// `reg_owner` is the instruction that gets associated with the register in the register table. |
| 1681 | 1731 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 1682 | 1732 | fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { |
| 1683 | | const reg = try self.register_manager.allocReg(reg_owner, gp); |
| 1733 | const ty = self.typeOfIndex(reg_owner); |
| 1734 | const reg = try self.register_manager.allocReg(reg_owner, self.regGeneralClassForType(ty)); |
| 1684 | 1735 | try self.genSetReg(self.typeOfIndex(reg_owner), reg, mcv); |
| 1685 | 1736 | return MCValue{ .register = reg }; |
| 1686 | 1737 | } |
| ... | ... | @@ -1797,7 +1848,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1797 | 1848 | if (self.reuseOperand(inst, ty_op.operand, 0, operand) and operand == .register) |
| 1798 | 1849 | operand.register |
| 1799 | 1850 | else |
| 1800 | | try self.register_manager.allocReg(inst, gp); |
| 1851 | (try self.allocRegOrMem(inst, true)).register; |
| 1801 | 1852 | |
| 1802 | 1853 | _ = try self.addInst(.{ |
| 1803 | 1854 | .tag = .pseudo, |
| ... | ... | @@ -1843,7 +1894,7 @@ fn airMinMax( |
| 1843 | 1894 | const lhs_reg, const lhs_lock = blk: { |
| 1844 | 1895 | if (lhs == .register) break :blk .{ lhs.register, self.register_manager.lockReg(lhs.register) }; |
| 1845 | 1896 | |
| 1846 | | const lhs_reg, const lhs_lock = try self.allocReg(); |
| 1897 | const lhs_reg, const lhs_lock = try self.allocReg(.int); |
| 1847 | 1898 | try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1848 | 1899 | break :blk .{ lhs_reg, lhs_lock }; |
| 1849 | 1900 | }; |
| ... | ... | @@ -1852,16 +1903,16 @@ fn airMinMax( |
| 1852 | 1903 | const rhs_reg, const rhs_lock = blk: { |
| 1853 | 1904 | if (rhs == .register) break :blk .{ rhs.register, self.register_manager.lockReg(rhs.register) }; |
| 1854 | 1905 | |
| 1855 | | const rhs_reg, const rhs_lock = try self.allocReg(); |
| 1906 | const rhs_reg, const rhs_lock = try self.allocReg(.int); |
| 1856 | 1907 | try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 1857 | 1908 | break :blk .{ rhs_reg, rhs_lock }; |
| 1858 | 1909 | }; |
| 1859 | 1910 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1860 | 1911 | |
| 1861 | | const mask_reg, const mask_lock = try self.allocReg(); |
| 1912 | const mask_reg, const mask_lock = try self.allocReg(.int); |
| 1862 | 1913 | defer self.register_manager.unlockReg(mask_lock); |
| 1863 | 1914 | |
| 1864 | | const result_reg, const result_lock = try self.allocReg(); |
| 1915 | const result_reg, const result_lock = try self.allocReg(.int); |
| 1865 | 1916 | defer self.register_manager.unlockReg(result_lock); |
| 1866 | 1917 | |
| 1867 | 1918 | _ = try self.addInst(.{ |
| ... | ... | @@ -1955,20 +2006,6 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 1955 | 2006 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1956 | 2007 | } |
| 1957 | 2008 | |
| 1958 | | /// For all your binary operation needs, this function will generate |
| 1959 | | /// the corresponding Mir instruction(s). Returns the location of the |
| 1960 | | /// result. |
| 1961 | | /// |
| 1962 | | /// If the binary operation itself happens to be an Air instruction, |
| 1963 | | /// pass the corresponding index in the inst parameter. That helps |
| 1964 | | /// this function do stuff like reusing operands. |
| 1965 | | /// |
| 1966 | | /// This function does not do any lowering to Mir itself, but instead |
| 1967 | | /// looks at the lhs and rhs and determines which kind of lowering |
| 1968 | | /// would be best suitable and then delegates the lowering to other |
| 1969 | | /// functions. |
| 1970 | | /// |
| 1971 | | /// `maybe_inst` **needs** to be a bin_op, make sure of that. |
| 1972 | 2009 | fn binOp( |
| 1973 | 2010 | self: *Self, |
| 1974 | 2011 | tag: Air.Inst.Tag, |
| ... | ... | @@ -1991,11 +2028,18 @@ fn binOp( |
| 1991 | 2028 | .cmp_lt, |
| 1992 | 2029 | .cmp_lte, |
| 1993 | 2030 | => { |
| 2031 | assert(lhs_ty.eql(rhs_ty, zcu)); |
| 1994 | 2032 | switch (lhs_ty.zigTypeTag(zcu)) { |
| 1995 | | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 2033 | .Float => { |
| 2034 | const float_bits = lhs_ty.floatBits(zcu.getTarget()); |
| 2035 | if (float_bits <= 32) { |
| 2036 | return self.binOpFloat(tag, lhs, lhs_ty, rhs, rhs_ty); |
| 2037 | } else { |
| 2038 | return self.fail("TODO: binary operations for floats with bits > 32", .{}); |
| 2039 | } |
| 2040 | }, |
| 1996 | 2041 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1997 | 2042 | .Int, .Enum, .ErrorSet => { |
| 1998 | | assert(lhs_ty.eql(rhs_ty, zcu)); |
| 1999 | 2043 | const int_info = lhs_ty.intInfo(zcu); |
| 2000 | 2044 | if (int_info.bits <= 64) { |
| 2001 | 2045 | return self.binOpRegister(tag, lhs, lhs_ty, rhs, rhs_ty); |
| ... | ... | @@ -2071,14 +2115,7 @@ fn binOp( |
| 2071 | 2115 | else => return self.fail("TODO binOp {}", .{tag}), |
| 2072 | 2116 | } |
| 2073 | 2117 | } |
| 2074 | | /// Don't call this function directly. Use binOp instead. |
| 2075 | | /// |
| 2076 | | /// Calling this function signals an intention to generate a Mir |
| 2077 | | /// instruction of the form |
| 2078 | | /// |
| 2079 | | /// op dest, lhs, rhs |
| 2080 | | /// |
| 2081 | | /// Asserts that generating an instruction of that form is possible. |
| 2118 | |
| 2082 | 2119 | fn binOpRegister( |
| 2083 | 2120 | self: *Self, |
| 2084 | 2121 | tag: Air.Inst.Tag, |
| ... | ... | @@ -2087,25 +2124,13 @@ fn binOpRegister( |
| 2087 | 2124 | rhs: MCValue, |
| 2088 | 2125 | rhs_ty: Type, |
| 2089 | 2126 | ) !MCValue { |
| 2090 | | const lhs_reg, const lhs_lock = blk: { |
| 2091 | | if (lhs == .register) break :blk .{ lhs.register, self.register_manager.lockReg(lhs.register) }; |
| 2092 | | |
| 2093 | | const lhs_reg, const lhs_lock = try self.allocReg(); |
| 2094 | | try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2095 | | break :blk .{ lhs_reg, lhs_lock }; |
| 2096 | | }; |
| 2127 | const lhs_reg, const lhs_lock = try self.promoteReg(lhs_ty, lhs); |
| 2097 | 2128 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2098 | 2129 | |
| 2099 | | const rhs_reg, const rhs_lock = blk: { |
| 2100 | | if (rhs == .register) break :blk .{ rhs.register, self.register_manager.lockReg(rhs.register) }; |
| 2101 | | |
| 2102 | | const rhs_reg, const rhs_lock = try self.allocReg(); |
| 2103 | | try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2104 | | break :blk .{ rhs_reg, rhs_lock }; |
| 2105 | | }; |
| 2130 | const rhs_reg, const rhs_lock = try self.promoteReg(rhs_ty, rhs); |
| 2106 | 2131 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2107 | 2132 | |
| 2108 | | const dest_reg, const dest_lock = try self.allocReg(); |
| 2133 | const dest_reg, const dest_lock = try self.allocReg(.int); |
| 2109 | 2134 | defer self.register_manager.unlockReg(dest_lock); |
| 2110 | 2135 | |
| 2111 | 2136 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| ... | ... | @@ -2184,7 +2209,50 @@ fn binOpRegister( |
| 2184 | 2209 | else => unreachable, |
| 2185 | 2210 | } |
| 2186 | 2211 | |
| 2187 | | // generate the struct for OF checks |
| 2212 | return MCValue{ .register = dest_reg }; |
| 2213 | } |
| 2214 | |
| 2215 | fn binOpFloat( |
| 2216 | self: *Self, |
| 2217 | tag: Air.Inst.Tag, |
| 2218 | lhs: MCValue, |
| 2219 | lhs_ty: Type, |
| 2220 | rhs: MCValue, |
| 2221 | rhs_ty: Type, |
| 2222 | ) !MCValue { |
| 2223 | const zcu = self.bin_file.comp.module.?; |
| 2224 | const float_bits = lhs_ty.floatBits(zcu.getTarget()); |
| 2225 | |
| 2226 | const lhs_reg, const lhs_lock = try self.promoteReg(lhs_ty, lhs); |
| 2227 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2228 | |
| 2229 | const rhs_reg, const rhs_lock = try self.promoteReg(rhs_ty, rhs); |
| 2230 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2231 | |
| 2232 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2233 | .add => if (float_bits == 32) .fadds else .faddd, |
| 2234 | .cmp_eq => if (float_bits == 32) .feqs else .feqd, |
| 2235 | else => return self.fail("TODO: binOpFloat mir_tag {s}", .{@tagName(tag)}), |
| 2236 | }; |
| 2237 | |
| 2238 | const return_class: abi.RegisterClass = switch (tag) { |
| 2239 | .add => .float, |
| 2240 | .cmp_eq => .int, |
| 2241 | else => unreachable, |
| 2242 | }; |
| 2243 | |
| 2244 | const dest_reg, const dest_lock = try self.allocReg(return_class); |
| 2245 | defer self.register_manager.unlockReg(dest_lock); |
| 2246 | |
| 2247 | _ = try self.addInst(.{ |
| 2248 | .tag = mir_tag, |
| 2249 | .ops = .rrr, |
| 2250 | .data = .{ .r_type = .{ |
| 2251 | .rd = dest_reg, |
| 2252 | .rs1 = lhs_reg, |
| 2253 | .rs2 = rhs_reg, |
| 2254 | } }, |
| 2255 | }); |
| 2188 | 2256 | |
| 2189 | 2257 | return MCValue{ .register = dest_reg }; |
| 2190 | 2258 | } |
| ... | ... | @@ -2279,7 +2347,7 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2279 | 2347 | |
| 2280 | 2348 | const shift_amount: u6 = @intCast(Type.usize.bitSize(zcu) - int_info.bits); |
| 2281 | 2349 | |
| 2282 | | const shift_reg, const shift_lock = try self.allocReg(); |
| 2350 | const shift_reg, const shift_lock = try self.allocReg(.int); |
| 2283 | 2351 | defer self.register_manager.unlockReg(shift_lock); |
| 2284 | 2352 | |
| 2285 | 2353 | _ = try self.addInst(.{ |
| ... | ... | @@ -2357,25 +2425,13 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2357 | 2425 | const result_mcv = try self.allocRegOrMem(inst, false); |
| 2358 | 2426 | const offset = result_mcv.load_frame; |
| 2359 | 2427 | |
| 2360 | | const lhs_reg, const lhs_lock = blk: { |
| 2361 | | if (lhs == .register) break :blk .{ lhs.register, self.register_manager.lockReg(lhs.register) }; |
| 2362 | | |
| 2363 | | const lhs_reg, const lhs_lock = try self.allocReg(); |
| 2364 | | try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2365 | | break :blk .{ lhs_reg, lhs_lock }; |
| 2366 | | }; |
| 2428 | const lhs_reg, const lhs_lock = try self.promoteReg(lhs_ty, lhs); |
| 2367 | 2429 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2368 | 2430 | |
| 2369 | | const rhs_reg, const rhs_lock = blk: { |
| 2370 | | if (rhs == .register) break :blk .{ rhs.register, self.register_manager.lockReg(rhs.register) }; |
| 2371 | | |
| 2372 | | const rhs_reg, const rhs_lock = try self.allocReg(); |
| 2373 | | try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2374 | | break :blk .{ rhs_reg, rhs_lock }; |
| 2375 | | }; |
| 2431 | const rhs_reg, const rhs_lock = try self.promoteReg(rhs_ty, rhs); |
| 2376 | 2432 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2377 | 2433 | |
| 2378 | | const dest_reg, const dest_lock = try self.allocReg(); |
| 2434 | const dest_reg, const dest_lock = try self.allocReg(.int); |
| 2379 | 2435 | defer self.register_manager.unlockReg(dest_lock); |
| 2380 | 2436 | |
| 2381 | 2437 | switch (int_info.signedness) { |
| ... | ... | @@ -2503,18 +2559,12 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2503 | 2559 | 1...8 => { |
| 2504 | 2560 | const max_val = std.math.pow(u16, 2, int_info.bits) - 1; |
| 2505 | 2561 | |
| 2506 | | const overflow_reg, const overflow_lock = try self.allocReg(); |
| 2507 | | defer self.register_manager.unlockReg(overflow_lock); |
| 2508 | | |
| 2509 | | const add_reg, const add_lock = blk: { |
| 2510 | | if (dest == .register) break :blk .{ dest.register, null }; |
| 2511 | | |
| 2512 | | const add_reg, const add_lock = try self.allocReg(); |
| 2513 | | try self.genSetReg(lhs_ty, add_reg, dest); |
| 2514 | | break :blk .{ add_reg, add_lock }; |
| 2515 | | }; |
| 2562 | const add_reg, const add_lock = try self.promoteReg(lhs_ty, lhs); |
| 2516 | 2563 | defer if (add_lock) |lock| self.register_manager.unlockReg(lock); |
| 2517 | 2564 | |
| 2565 | const overflow_reg, const overflow_lock = try self.allocReg(.int); |
| 2566 | defer self.register_manager.unlockReg(overflow_lock); |
| 2567 | |
| 2518 | 2568 | _ = try self.addInst(.{ |
| 2519 | 2569 | .tag = .andi, |
| 2520 | 2570 | .ops = .rri, |
| ... | ... | @@ -2595,25 +2645,13 @@ fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { |
| 2595 | 2645 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 2596 | 2646 | const rhs_ty = self.typeOf(bin_op.rhs); |
| 2597 | 2647 | |
| 2598 | | const lhs_reg, const lhs_lock = blk: { |
| 2599 | | if (lhs == .register) break :blk .{ lhs.register, self.register_manager.lockReg(lhs.register) }; |
| 2600 | | |
| 2601 | | const lhs_reg, const lhs_lock = try self.allocReg(); |
| 2602 | | try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2603 | | break :blk .{ lhs_reg, lhs_lock }; |
| 2604 | | }; |
| 2648 | const lhs_reg, const lhs_lock = try self.promoteReg(lhs_ty, lhs); |
| 2605 | 2649 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2606 | 2650 | |
| 2607 | | const rhs_reg, const rhs_lock = blk: { |
| 2608 | | if (rhs == .register) break :blk .{ rhs.register, self.register_manager.lockReg(rhs.register) }; |
| 2609 | | |
| 2610 | | const rhs_reg, const rhs_lock = try self.allocReg(); |
| 2611 | | try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2612 | | break :blk .{ rhs_reg, rhs_lock }; |
| 2613 | | }; |
| 2651 | const rhs_reg, const rhs_lock = try self.promoteReg(rhs_ty, rhs); |
| 2614 | 2652 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2615 | 2653 | |
| 2616 | | const dest_reg, const dest_lock = try self.allocReg(); |
| 2654 | const dest_reg, const dest_lock = try self.allocReg(.int); |
| 2617 | 2655 | defer self.register_manager.unlockReg(dest_lock); |
| 2618 | 2656 | |
| 2619 | 2657 | _ = try self.addInst(.{ |
| ... | ... | @@ -2640,25 +2678,13 @@ fn airBitOr(self: *Self, inst: Air.Inst.Index) !void { |
| 2640 | 2678 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 2641 | 2679 | const rhs_ty = self.typeOf(bin_op.rhs); |
| 2642 | 2680 | |
| 2643 | | const lhs_reg, const lhs_lock = blk: { |
| 2644 | | if (lhs == .register) break :blk .{ lhs.register, self.register_manager.lockReg(lhs.register) }; |
| 2645 | | |
| 2646 | | const lhs_reg, const lhs_lock = try self.allocReg(); |
| 2647 | | try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2648 | | break :blk .{ lhs_reg, lhs_lock }; |
| 2649 | | }; |
| 2681 | const lhs_reg, const lhs_lock = try self.promoteReg(lhs_ty, lhs); |
| 2650 | 2682 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2651 | 2683 | |
| 2652 | | const rhs_reg, const rhs_lock = blk: { |
| 2653 | | if (rhs == .register) break :blk .{ rhs.register, self.register_manager.lockReg(rhs.register) }; |
| 2654 | | |
| 2655 | | const rhs_reg, const rhs_lock = try self.allocReg(); |
| 2656 | | try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2657 | | break :blk .{ rhs_reg, rhs_lock }; |
| 2658 | | }; |
| 2684 | const rhs_reg, const rhs_lock = try self.promoteReg(rhs_ty, rhs); |
| 2659 | 2685 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 2660 | 2686 | |
| 2661 | | const dest_reg, const dest_lock = try self.allocReg(); |
| 2687 | const dest_reg, const dest_lock = try self.allocReg(.int); |
| 2662 | 2688 | defer self.register_manager.unlockReg(dest_lock); |
| 2663 | 2689 | |
| 2664 | 2690 | _ = try self.addInst(.{ |
| ... | ... | @@ -3102,7 +3128,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3102 | 3128 | const elem_ty = array_ty.childType(zcu); |
| 3103 | 3129 | const elem_abi_size = elem_ty.abiSize(zcu); |
| 3104 | 3130 | |
| 3105 | | const addr_reg, const addr_reg_lock = try self.allocReg(); |
| 3131 | const addr_reg, const addr_reg_lock = try self.allocReg(.int); |
| 3106 | 3132 | defer self.register_manager.unlockReg(addr_reg_lock); |
| 3107 | 3133 | |
| 3108 | 3134 | switch (array_mcv) { |
| ... | ... | @@ -3211,46 +3237,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void { |
| 3211 | 3237 | |
| 3212 | 3238 | fn airCtz(self: *Self, inst: Air.Inst.Index) !void { |
| 3213 | 3239 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3214 | | const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { |
| 3215 | | const operand = try self.resolveInst(ty_op.operand); |
| 3216 | | const operand_ty = self.typeOf(ty_op.operand); |
| 3217 | | |
| 3218 | | const dest_reg = try self.register_manager.allocReg(inst, gp); |
| 3219 | | |
| 3220 | | const source_reg, const source_lock = blk: { |
| 3221 | | if (operand == .register) break :blk .{ operand.register, null }; |
| 3222 | | |
| 3223 | | const source_reg, const source_lock = try self.allocReg(); |
| 3224 | | try self.genSetReg(operand_ty, source_reg, operand); |
| 3225 | | break :blk .{ source_reg, source_lock }; |
| 3226 | | }; |
| 3227 | | defer if (source_lock) |lock| self.register_manager.unlockReg(lock); |
| 3228 | | |
| 3229 | | // TODO: the B extension for RISCV should have the ctz instruction, and we should use it. |
| 3230 | | |
| 3231 | | try self.ctz(source_reg, dest_reg, operand_ty); |
| 3232 | | |
| 3233 | | break :result .{ .register = dest_reg }; |
| 3234 | | }; |
| 3235 | | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3236 | | } |
| 3237 | | |
| 3238 | | fn ctz(self: *Self, src: Register, dst: Register, ty: Type) !void { |
| 3239 | | const zcu = self.bin_file.comp.module.?; |
| 3240 | | const length = (ty.abiSize(zcu) * 8) - 1; |
| 3241 | | |
| 3242 | | const count_reg, const count_lock = try self.allocReg(); |
| 3243 | | defer self.register_manager.unlockReg(count_lock); |
| 3244 | | |
| 3245 | | const len_reg, const len_lock = try self.allocReg(); |
| 3246 | | defer self.register_manager.unlockReg(len_lock); |
| 3247 | | |
| 3248 | | try self.genSetReg(Type.usize, count_reg, .{ .immediate = 0 }); |
| 3249 | | try self.genSetReg(Type.usize, len_reg, .{ .immediate = length }); |
| 3250 | | |
| 3251 | | _ = src; |
| 3252 | | _ = dst; |
| 3253 | | |
| 3240 | _ = ty_op; |
| 3254 | 3241 | return self.fail("TODO: finish ctz", .{}); |
| 3255 | 3242 | } |
| 3256 | 3243 | |
| ... | ... | @@ -3267,38 +3254,18 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void { |
| 3267 | 3254 | const ty = self.typeOf(ty_op.operand); |
| 3268 | 3255 | const scalar_ty = ty.scalarType(zcu); |
| 3269 | 3256 | const operand = try self.resolveInst(ty_op.operand); |
| 3257 | _ = operand; |
| 3270 | 3258 | |
| 3271 | 3259 | switch (scalar_ty.zigTypeTag(zcu)) { |
| 3272 | 3260 | .Int => if (ty.zigTypeTag(zcu) == .Vector) { |
| 3273 | 3261 | return self.fail("TODO implement airAbs for {}", .{ty.fmt(zcu)}); |
| 3274 | 3262 | } else { |
| 3275 | | const int_bits = ty.intInfo(zcu).bits; |
| 3276 | | |
| 3277 | | if (int_bits > 32) { |
| 3278 | | return self.fail("TODO: airAbs for larger than 32 bits", .{}); |
| 3279 | | } |
| 3280 | | |
| 3281 | | // promote the src into a register |
| 3282 | | const src_mcv = try self.copyToNewRegister(inst, operand); |
| 3283 | | // temp register for shift |
| 3284 | | const temp_reg = try self.register_manager.allocReg(inst, gp); |
| 3285 | | |
| 3286 | | _ = try self.addInst(.{ |
| 3287 | | .tag = .abs, |
| 3288 | | .ops = .rri, |
| 3289 | | .data = .{ |
| 3290 | | .i_type = .{ |
| 3291 | | .rs1 = src_mcv.register, |
| 3292 | | .rd = temp_reg, |
| 3293 | | .imm12 = Immediate.s(int_bits - 1), |
| 3294 | | }, |
| 3295 | | }, |
| 3296 | | }); |
| 3297 | | |
| 3298 | | break :result src_mcv; |
| 3263 | return self.fail("TODO: implement airAbs for Int", .{}); |
| 3299 | 3264 | }, |
| 3300 | 3265 | else => return self.fail("TODO: implement airAbs {}", .{scalar_ty.fmt(zcu)}), |
| 3301 | 3266 | } |
| 3267 | |
| 3268 | break :result .{.unreach}; |
| 3302 | 3269 | }; |
| 3303 | 3270 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3304 | 3271 | } |
| ... | ... | @@ -3317,15 +3284,24 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { |
| 3317 | 3284 | return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); |
| 3318 | 3285 | } |
| 3319 | 3286 | |
| 3320 | | const dest_reg = try self.register_manager.allocReg(null, gp); |
| 3321 | | try self.genSetReg(ty, dest_reg, operand); |
| 3322 | | |
| 3323 | | const dest_mcv: MCValue = .{ .register = dest_reg }; |
| 3287 | const dest_mcv = try self.copyToNewRegister(inst, operand); |
| 3288 | const dest_reg = dest_mcv.register; |
| 3324 | 3289 | |
| 3325 | 3290 | switch (int_bits) { |
| 3326 | 3291 | 16 => { |
| 3327 | | const temp = try self.binOp(.shr, dest_mcv, ty, .{ .immediate = 8 }, Type.u8); |
| 3328 | | assert(temp == .register); |
| 3292 | const temp_reg, const temp_lock = try self.allocReg(.int); |
| 3293 | defer self.register_manager.unlockReg(temp_lock); |
| 3294 | |
| 3295 | _ = try self.addInst(.{ |
| 3296 | .tag = .srli, |
| 3297 | .ops = .rri, |
| 3298 | .data = .{ .i_type = .{ |
| 3299 | .imm12 = Immediate.s(8), |
| 3300 | .rd = temp_reg, |
| 3301 | .rs1 = dest_reg, |
| 3302 | } }, |
| 3303 | }); |
| 3304 | |
| 3329 | 3305 | _ = try self.addInst(.{ |
| 3330 | 3306 | .tag = .slli, |
| 3331 | 3307 | .ops = .rri, |
| ... | ... | @@ -3341,7 +3317,7 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { |
| 3341 | 3317 | .data = .{ .r_type = .{ |
| 3342 | 3318 | .rd = dest_reg, |
| 3343 | 3319 | .rs1 = dest_reg, |
| 3344 | | .rs2 = temp.register, |
| 3320 | .rs2 = temp_reg, |
| 3345 | 3321 | } }, |
| 3346 | 3322 | }); |
| 3347 | 3323 | }, |
| ... | ... | @@ -3360,11 +3336,12 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { |
| 3360 | 3336 | } |
| 3361 | 3337 | |
| 3362 | 3338 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 3339 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| 3363 | 3340 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 3364 | 3341 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 3365 | 3342 | .unreach |
| 3366 | 3343 | else |
| 3367 | | return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); |
| 3344 | return self.fail("TODO implementairUnaryMath {s} for {}", .{ @tagName(tag), self.target.cpu.arch }); |
| 3368 | 3345 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3369 | 3346 | } |
| 3370 | 3347 | |
| ... | ... | @@ -3640,7 +3617,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3640 | 3617 | @intCast(field_bit_size), |
| 3641 | 3618 | ); |
| 3642 | 3619 | |
| 3643 | | const dst_reg, const dst_lock = try self.allocReg(); |
| 3620 | const dst_reg, const dst_lock = try self.allocReg(.int); |
| 3644 | 3621 | const dst_mcv = MCValue{ .register = dst_reg }; |
| 3645 | 3622 | defer self.register_manager.unlockReg(dst_lock); |
| 3646 | 3623 | |
| ... | ... | @@ -3658,7 +3635,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3658 | 3635 | break :dst dst_mcv; |
| 3659 | 3636 | }; |
| 3660 | 3637 | if (field_abi_size * 8 > field_bit_size and dst_mcv.isMemory()) { |
| 3661 | | const tmp_reg, const tmp_lock = try self.allocReg(); |
| 3638 | const tmp_reg, const tmp_lock = try self.allocReg(.int); |
| 3662 | 3639 | defer self.register_manager.unlockReg(tmp_lock); |
| 3663 | 3640 | |
| 3664 | 3641 | const hi_mcv = |
| ... | ... | @@ -3972,7 +3949,7 @@ fn genCall( |
| 3972 | 3949 | } |
| 3973 | 3950 | } else { |
| 3974 | 3951 | assert(self.typeOf(callee).zigTypeTag(zcu) == .Pointer); |
| 3975 | | const addr_reg, const addr_lock = try self.allocReg(); |
| 3952 | const addr_reg, const addr_lock = try self.allocReg(.int); |
| 3976 | 3953 | defer self.register_manager.unlockReg(addr_lock); |
| 3977 | 3954 | try self.genSetReg(Type.usize, addr_reg, .{ .air_ref = callee }); |
| 3978 | 3955 | |
| ... | ... | @@ -4072,32 +4049,49 @@ fn airCmp(self: *Self, inst: Air.Inst.Index) !void { |
| 4072 | 4049 | const rhs = try self.resolveInst(bin_op.rhs); |
| 4073 | 4050 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 4074 | 4051 | |
| 4075 | | const int_ty = switch (lhs_ty.zigTypeTag(zcu)) { |
| 4076 | | .Vector => unreachable, // Handled by cmp_vector. |
| 4077 | | .Enum => lhs_ty.intTagType(zcu), |
| 4078 | | .Int => lhs_ty, |
| 4079 | | .Bool => Type.u1, |
| 4080 | | .Pointer => Type.usize, |
| 4081 | | .ErrorSet => Type.u16, |
| 4082 | | .Optional => blk: { |
| 4083 | | const payload_ty = lhs_ty.optionalChild(zcu); |
| 4084 | | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4085 | | break :blk Type.u1; |
| 4086 | | } else if (lhs_ty.isPtrLikeOptional(zcu)) { |
| 4087 | | break :blk Type.usize; |
| 4052 | switch (lhs_ty.zigTypeTag(zcu)) { |
| 4053 | .Int, |
| 4054 | .Enum, |
| 4055 | .Bool, |
| 4056 | .Pointer, |
| 4057 | .ErrorSet, |
| 4058 | .Optional, |
| 4059 | => { |
| 4060 | const int_ty = switch (lhs_ty.zigTypeTag(zcu)) { |
| 4061 | .Enum => lhs_ty.intTagType(zcu), |
| 4062 | .Int => lhs_ty, |
| 4063 | .Bool => Type.u1, |
| 4064 | .Pointer => Type.usize, |
| 4065 | .ErrorSet => Type.u16, |
| 4066 | .Optional => blk: { |
| 4067 | const payload_ty = lhs_ty.optionalChild(zcu); |
| 4068 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4069 | break :blk Type.u1; |
| 4070 | } else if (lhs_ty.isPtrLikeOptional(zcu)) { |
| 4071 | break :blk Type.usize; |
| 4072 | } else { |
| 4073 | return self.fail("TODO riscv cmp non-pointer optionals", .{}); |
| 4074 | } |
| 4075 | }, |
| 4076 | else => unreachable, |
| 4077 | }; |
| 4078 | |
| 4079 | const int_info = int_ty.intInfo(zcu); |
| 4080 | if (int_info.bits <= 64) { |
| 4081 | break :result try self.binOp(tag, lhs, int_ty, rhs, int_ty); |
| 4088 | 4082 | } else { |
| 4089 | | return self.fail("TODO riscv cmp non-pointer optionals", .{}); |
| 4083 | return self.fail("TODO riscv cmp for ints > 64 bits", .{}); |
| 4090 | 4084 | } |
| 4091 | 4085 | }, |
| 4092 | | .Float => return self.fail("TODO riscv cmp floats", .{}), |
| 4093 | | else => unreachable, |
| 4094 | | }; |
| 4086 | .Float => { |
| 4087 | const float_bits = lhs_ty.floatBits(self.target.*); |
| 4088 | if (float_bits > 32) { |
| 4089 | return self.fail("TODO: airCmp float > 32 bits", .{}); |
| 4090 | } |
| 4095 | 4091 | |
| 4096 | | const int_info = int_ty.intInfo(zcu); |
| 4097 | | if (int_info.bits <= 64) { |
| 4098 | | break :result try self.binOp(tag, lhs, int_ty, rhs, int_ty); |
| 4099 | | } else { |
| 4100 | | return self.fail("TODO riscv cmp for ints > 64 bits", .{}); |
| 4092 | break :result try self.binOpFloat(tag, lhs, lhs_ty, rhs, lhs_ty); |
| 4093 | }, |
| 4094 | else => unreachable, |
| 4101 | 4095 | } |
| 4102 | 4096 | }; |
| 4103 | 4097 | |
| ... | ... | @@ -4716,25 +4710,13 @@ fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void { |
| 4716 | 4710 | const lhs_ty = Type.bool; |
| 4717 | 4711 | const rhs_ty = Type.bool; |
| 4718 | 4712 | |
| 4719 | | const lhs_reg, const lhs_lock = blk: { |
| 4720 | | if (lhs == .register) break :blk .{ lhs.register, self.register_manager.lockReg(lhs.register) }; |
| 4721 | | |
| 4722 | | const lhs_reg, const lhs_lock = try self.allocReg(); |
| 4723 | | try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 4724 | | break :blk .{ lhs_reg, lhs_lock }; |
| 4725 | | }; |
| 4713 | const lhs_reg, const lhs_lock = try self.promoteReg(lhs_ty, lhs); |
| 4726 | 4714 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 4727 | 4715 | |
| 4728 | | const rhs_reg, const rhs_lock = blk: { |
| 4729 | | if (rhs == .register) break :blk .{ rhs.register, self.register_manager.lockReg(rhs.register) }; |
| 4730 | | |
| 4731 | | const rhs_reg, const rhs_lock = try self.allocReg(); |
| 4732 | | try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 4733 | | break :blk .{ rhs_reg, rhs_lock }; |
| 4734 | | }; |
| 4716 | const rhs_reg, const rhs_lock = try self.promoteReg(rhs_ty, rhs); |
| 4735 | 4717 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 4736 | 4718 | |
| 4737 | | const result_reg, const result_lock = try self.allocReg(); |
| 4719 | const result_reg, const result_lock = try self.allocReg(.int); |
| 4738 | 4720 | defer self.register_manager.unlockReg(result_lock); |
| 4739 | 4721 | |
| 4740 | 4722 | _ = try self.addInst(.{ |
| ... | ... | @@ -4881,7 +4863,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 4881 | 4863 | return self.finishAirResult(inst, result); |
| 4882 | 4864 | } |
| 4883 | 4865 | |
| 4884 | | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. |
| 4866 | /// Sets the value of `dst_mcv` to the value of `src_mcv`. |
| 4885 | 4867 | fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 4886 | 4868 | const zcu = self.bin_file.comp.module.?; |
| 4887 | 4869 | |
| ... | ... | @@ -4890,7 +4872,7 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 4890 | 4872 | |
| 4891 | 4873 | if (!dst_mcv.isMutable()) { |
| 4892 | 4874 | // panic so we can see the trace |
| 4893 | | return self.fail("tried to genCopy immutable: {s}", .{@tagName(dst_mcv)}); |
| 4875 | return std.debug.panic("tried to genCopy immutable: {s}", .{@tagName(dst_mcv)}); |
| 4894 | 4876 | } |
| 4895 | 4877 | |
| 4896 | 4878 | switch (dst_mcv) { |
| ... | ... | @@ -4924,13 +4906,12 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 4924 | 4906 | ), |
| 4925 | 4907 | .memory => return self.fail("TODO: genCopy memory", .{}), |
| 4926 | 4908 | .register_pair => |dst_regs| { |
| 4927 | | const src_info: ?struct { addr_reg: Register, addr_lock: RegisterLock } = switch (src_mcv) { |
| 4909 | const src_info: ?struct { addr_reg: Register, addr_lock: ?RegisterLock } = switch (src_mcv) { |
| 4928 | 4910 | .register_pair, .memory, .indirect, .load_frame => null, |
| 4929 | 4911 | .load_symbol => src: { |
| 4930 | | const src_addr_reg, const src_addr_lock = try self.allocReg(); |
| 4912 | const src_addr_reg, const src_addr_lock = try self.promoteReg(Type.usize, src_mcv.address()); |
| 4931 | 4913 | errdefer self.register_manager.unlockReg(src_addr_lock); |
| 4932 | 4914 | |
| 4933 | | try self.genSetReg(Type.usize, src_addr_reg, src_mcv.address()); |
| 4934 | 4915 | break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock }; |
| 4935 | 4916 | }, |
| 4936 | 4917 | .air_ref => |src_ref| return self.genCopy( |
| ... | ... | @@ -4940,7 +4921,12 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 4940 | 4921 | ), |
| 4941 | 4922 | else => unreachable, |
| 4942 | 4923 | }; |
| 4943 | | defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock); |
| 4924 | |
| 4925 | defer if (src_info) |info| { |
| 4926 | if (info.addr_lock) |lock| { |
| 4927 | self.register_manager.unlockReg(lock); |
| 4928 | } |
| 4929 | }; |
| 4944 | 4930 | |
| 4945 | 4931 | var part_disp: i32 = 0; |
| 4946 | 4932 | for (dst_regs, try self.splitType(ty), 0..) |dst_reg, dst_ty, part_i| { |
| ... | ... | @@ -4966,7 +4952,7 @@ fn genInlineMemcpy( |
| 4966 | 4952 | src_ptr: MCValue, |
| 4967 | 4953 | len: MCValue, |
| 4968 | 4954 | ) !void { |
| 4969 | | const regs = try self.register_manager.allocRegs(4, .{null} ** 4, tp); |
| 4955 | const regs = try self.register_manager.allocRegs(4, .{null} ** 4, abi.Registers.Integer.temporary); |
| 4970 | 4956 | const locks = self.register_manager.lockRegsAssumeUnused(4, regs); |
| 4971 | 4957 | defer for (locks) |lock| self.register_manager.unlockReg(lock); |
| 4972 | 4958 | |
| ... | ... | @@ -5060,9 +5046,7 @@ fn genInlineMemcpy( |
| 5060 | 5046 | _ = try self.addInst(.{ |
| 5061 | 5047 | .tag = .pseudo, |
| 5062 | 5048 | .ops = .pseudo_j, |
| 5063 | | .data = .{ |
| 5064 | | .inst = first_inst, |
| 5065 | | }, |
| 5049 | .data = .{ .inst = first_inst }, |
| 5066 | 5050 | }); |
| 5067 | 5051 | } |
| 5068 | 5052 | |
| ... | ... | @@ -5072,7 +5056,7 @@ fn genInlineMemset( |
| 5072 | 5056 | src_value: MCValue, |
| 5073 | 5057 | len: MCValue, |
| 5074 | 5058 | ) !void { |
| 5075 | | const regs = try self.register_manager.allocRegs(3, .{null} ** 3, tp); |
| 5059 | const regs = try self.register_manager.allocRegs(3, .{null} ** 3, abi.Registers.Integer.temporary); |
| 5076 | 5060 | const locks = self.register_manager.lockRegsAssumeUnused(3, regs); |
| 5077 | 5061 | defer for (locks) |lock| self.register_manager.unlockReg(lock); |
| 5078 | 5062 | |
| ... | ... | @@ -5153,6 +5137,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5153 | 5137 | |
| 5154 | 5138 | if (abi_size > 8) return std.debug.panic("tried to set reg with size {}", .{abi_size}); |
| 5155 | 5139 | |
| 5140 | const dst_reg_class = reg.class(); |
| 5141 | |
| 5156 | 5142 | switch (src_mcv) { |
| 5157 | 5143 | .dead => unreachable, |
| 5158 | 5144 | .unreach, .none => return, // Nothing to do. |
| ... | ... | @@ -5163,6 +5149,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5163 | 5149 | return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); |
| 5164 | 5150 | }, |
| 5165 | 5151 | .immediate => |unsigned_x| { |
| 5152 | assert(dst_reg_class == .int); |
| 5153 | |
| 5166 | 5154 | const x: i64 = @bitCast(unsigned_x); |
| 5167 | 5155 | if (math.minInt(i12) <= x and x <= math.maxInt(i12)) { |
| 5168 | 5156 | _ = try self.addInst(.{ |
| ... | ... | @@ -5200,7 +5188,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5200 | 5188 | // TODO: use a more advanced myriad seq to do this without a reg. |
| 5201 | 5189 | // see: https://github.com/llvm/llvm-project/blob/081a66ffacfe85a37ff775addafcf3371e967328/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp#L224 |
| 5202 | 5190 | |
| 5203 | | const temp, const temp_lock = try self.allocReg(); |
| 5191 | const temp, const temp_lock = try self.allocReg(.int); |
| 5204 | 5192 | defer self.register_manager.unlockReg(temp_lock); |
| 5205 | 5193 | |
| 5206 | 5194 | const lo32: i32 = @truncate(x); |
| ... | ... | @@ -5236,6 +5224,19 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5236 | 5224 | if (src_reg.id() == reg.id()) |
| 5237 | 5225 | return; |
| 5238 | 5226 | |
| 5227 | const src_reg_class = src_reg.class(); |
| 5228 | |
| 5229 | if (src_reg_class == .float) { |
| 5230 | if (dst_reg_class == .float) { |
| 5231 | return self.fail("TODO: genSetReg float -> float", .{}); |
| 5232 | } |
| 5233 | |
| 5234 | assert(dst_reg_class == .int); // a bit of future proofing |
| 5235 | |
| 5236 | // to move from float -> int, we use FMV.X.W |
| 5237 | return self.fail("TODO: genSetReg float -> int", .{}); |
| 5238 | } |
| 5239 | |
| 5239 | 5240 | // mv reg, src_reg |
| 5240 | 5241 | _ = try self.addInst(.{ |
| 5241 | 5242 | .tag = .pseudo, |
| ... | ... | @@ -5309,11 +5310,19 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5309 | 5310 | }); |
| 5310 | 5311 | }, |
| 5311 | 5312 | .indirect => |reg_off| { |
| 5313 | const float_class = dst_reg_class == .float; |
| 5314 | |
| 5312 | 5315 | const load_tag: Mir.Inst.Tag = switch (abi_size) { |
| 5313 | | 1 => .lb, |
| 5314 | | 2 => .lh, |
| 5315 | | 4 => .lw, |
| 5316 | | 8 => .ld, |
| 5316 | 1 => if (float_class) |
| 5317 | unreachable // Zig does not support 8-bit floats |
| 5318 | else |
| 5319 | .lb, |
| 5320 | 2 => if (float_class) |
| 5321 | return self.fail("TODO: genSetReg indirect 16-bit float", .{}) |
| 5322 | else |
| 5323 | .lh, |
| 5324 | 4 => if (float_class) .flw else .lw, |
| 5325 | 8 => if (float_class) .fld else .ld, |
| 5317 | 5326 | else => return std.debug.panic("TODO: genSetReg for size {d}", .{abi_size}), |
| 5318 | 5327 | }; |
| 5319 | 5328 | |
| ... | ... | @@ -5336,15 +5345,18 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5336 | 5345 | .tag = .pseudo, |
| 5337 | 5346 | .ops = .pseudo_load_symbol, |
| 5338 | 5347 | .data = .{ .payload = try self.addExtra(Mir.LoadSymbolPayload{ |
| 5339 | | .register = reg.id(), |
| 5348 | .register = reg.encodeId(), |
| 5340 | 5349 | .atom_index = atom_index, |
| 5341 | 5350 | .sym_index = sym_off.sym, |
| 5342 | 5351 | }) }, |
| 5343 | 5352 | }); |
| 5344 | 5353 | }, |
| 5345 | 5354 | .load_symbol => { |
| 5346 | | try self.genSetReg(ty, reg, src_mcv.address()); |
| 5347 | | try self.genSetReg(ty, reg, .{ .indirect = .{ .reg = reg } }); |
| 5355 | const addr_reg, const addr_lock = try self.allocReg(.int); |
| 5356 | defer self.register_manager.unlockReg(addr_lock); |
| 5357 | |
| 5358 | try self.genSetReg(ty, addr_reg, src_mcv.address()); |
| 5359 | try self.genSetReg(ty, reg, .{ .indirect = .{ .reg = addr_reg } }); |
| 5348 | 5360 | }, |
| 5349 | 5361 | .air_ref => |ref| try self.genSetReg(ty, reg, try self.resolveInst(ref)), |
| 5350 | 5362 | else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}), |
| ... | ... | @@ -5386,7 +5398,8 @@ fn genSetMem( |
| 5386 | 5398 | => switch (abi_size) { |
| 5387 | 5399 | 0 => {}, |
| 5388 | 5400 | 1, 2, 4, 8 => { |
| 5389 | | const src_reg = try self.copyToTmpRegister(ty, src_mcv); |
| 5401 | // no matter what type, it should use an integer register |
| 5402 | const src_reg = try self.copyToTmpRegister(Type.usize, src_mcv); |
| 5390 | 5403 | const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); |
| 5391 | 5404 | defer self.register_manager.unlockReg(src_lock); |
| 5392 | 5405 | |
| ... | ... | @@ -5460,10 +5473,8 @@ fn genSetMem( |
| 5460 | 5473 | .immediate => { |
| 5461 | 5474 | // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with |
| 5462 | 5475 | // a register allocation. |
| 5463 | | const reg, const reg_lock = try self.allocReg(); |
| 5464 | | defer self.register_manager.unlockReg(reg_lock); |
| 5465 | | |
| 5466 | | try self.genSetReg(ty, reg, src_mcv); |
| 5476 | const reg, const reg_lock = try self.promoteReg(ty, src_mcv); |
| 5477 | defer if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 5467 | 5478 | |
| 5468 | 5479 | return self.genSetMem(base, disp, ty, .{ .register = reg }); |
| 5469 | 5480 | }, |
| ... | ... | @@ -5632,7 +5643,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| 5632 | 5643 | assert(len != 0); // prevented by Sema |
| 5633 | 5644 | try self.store(dst_ptr, src_val, elem_ptr_ty, elem_ty); |
| 5634 | 5645 | |
| 5635 | | const second_elem_ptr_reg, const second_elem_ptr_lock = try self.allocReg(); |
| 5646 | const second_elem_ptr_reg, const second_elem_ptr_lock = try self.allocReg(.int); |
| 5636 | 5647 | defer self.register_manager.unlockReg(second_elem_ptr_lock); |
| 5637 | 5648 | |
| 5638 | 5649 | const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg }; |
| ... | ... | @@ -5677,7 +5688,7 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { |
| 5677 | 5688 | const err_lock = self.register_manager.lockRegAssumeUnused(err_reg); |
| 5678 | 5689 | defer self.register_manager.unlockReg(err_lock); |
| 5679 | 5690 | |
| 5680 | | const addr_reg, const addr_lock = try self.allocReg(); |
| 5691 | const addr_reg, const addr_lock = try self.allocReg(.int); |
| 5681 | 5692 | defer self.register_manager.unlockReg(addr_lock); |
| 5682 | 5693 | |
| 5683 | 5694 | // this is now the base address of the error name table |
| ... | ... | @@ -5691,13 +5702,13 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { |
| 5691 | 5702 | return self.fail("TODO: riscv non-elf", .{}); |
| 5692 | 5703 | } |
| 5693 | 5704 | |
| 5694 | | const start_reg, const start_lock = try self.allocReg(); |
| 5705 | const start_reg, const start_lock = try self.allocReg(.int); |
| 5695 | 5706 | defer self.register_manager.unlockReg(start_lock); |
| 5696 | 5707 | |
| 5697 | | const end_reg, const end_lock = try self.allocReg(); |
| 5708 | const end_reg, const end_lock = try self.allocReg(.int); |
| 5698 | 5709 | defer self.register_manager.unlockReg(end_lock); |
| 5699 | 5710 | |
| 5700 | | // const tmp_reg, const tmp_lock = try self.allocReg(); |
| 5711 | // const tmp_reg, const tmp_lock = try self.allocReg(.int); |
| 5701 | 5712 | // defer self.register_manager.unlockReg(tmp_lock); |
| 5702 | 5713 | |
| 5703 | 5714 | // we move the base address forward by the following formula: base + (errno * 8) |
| ... | ... | @@ -6025,16 +6036,16 @@ fn resolveCallingConventionValues( |
| 6025 | 6036 | |
| 6026 | 6037 | for (classes) |class| switch (class) { |
| 6027 | 6038 | .integer => { |
| 6028 | | const ret_int_reg = abi.function_ret_regs[ret_int_reg_i]; |
| 6039 | const ret_int_reg = abi.Registers.Integer.function_ret_regs[ret_int_reg_i]; |
| 6029 | 6040 | ret_int_reg_i += 1; |
| 6030 | 6041 | |
| 6031 | 6042 | ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_int_reg }); |
| 6032 | 6043 | ret_tracking_i += 1; |
| 6033 | 6044 | }, |
| 6034 | 6045 | .memory => { |
| 6035 | | const ret_int_reg = abi.function_ret_regs[ret_int_reg_i]; |
| 6046 | const ret_int_reg = abi.Registers.Integer.function_ret_regs[ret_int_reg_i]; |
| 6036 | 6047 | ret_int_reg_i += 1; |
| 6037 | | const ret_indirect_reg = abi.function_arg_regs[param_int_reg_i]; |
| 6048 | const ret_indirect_reg = abi.Registers.Integer.function_arg_regs[param_int_reg_i]; |
| 6038 | 6049 | param_int_reg_i += 1; |
| 6039 | 6050 | |
| 6040 | 6051 | ret_tracking[ret_tracking_i] = .{ |
| ... | ... | @@ -6069,7 +6080,7 @@ fn resolveCallingConventionValues( |
| 6069 | 6080 | |
| 6070 | 6081 | for (classes) |class| switch (class) { |
| 6071 | 6082 | .integer => { |
| 6072 | | const param_int_regs = abi.function_arg_regs; |
| 6083 | const param_int_regs = abi.Registers.Integer.function_arg_regs; |
| 6073 | 6084 | if (param_int_reg_i >= param_int_regs.len) break; |
| 6074 | 6085 | |
| 6075 | 6086 | const param_int_reg = param_int_regs[param_int_reg_i]; |
| ... | ... | @@ -6079,7 +6090,7 @@ fn resolveCallingConventionValues( |
| 6079 | 6090 | arg_mcv_i += 1; |
| 6080 | 6091 | }, |
| 6081 | 6092 | .memory => { |
| 6082 | | const param_int_regs = abi.function_arg_regs; |
| 6093 | const param_int_regs = abi.Registers.Integer.function_arg_regs; |
| 6083 | 6094 | |
| 6084 | 6095 | const param_int_reg = param_int_regs[param_int_reg_i]; |
| 6085 | 6096 | param_int_reg_i += 1; |