| ... | ... | @@ -102,9 +102,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 102 | 102 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 103 | 103 | |
| 104 | 104 | const MCValue = union(enum) { |
| 105 | | /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. |
| 106 | | /// TODO Look into deleting this tag and using `dead` instead, since every use |
| 107 | | /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. |
| 105 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 |
| 106 | /// tag, etc. |
| 107 | /// |
| 108 | /// TODO Look into deleting this tag and using `dead` instead, |
| 109 | /// since every use of MCValue.none should be instead looking at |
| 110 | /// the type and noticing it is 0 bits. |
| 108 | 111 | none, |
| 109 | 112 | /// Control flow will not allow this value to be observed. |
| 110 | 113 | unreach, |
| ... | ... | @@ -113,28 +116,56 @@ const MCValue = union(enum) { |
| 113 | 116 | /// The value is undefined. |
| 114 | 117 | undef, |
| 115 | 118 | /// A pointer-sized integer that fits in a register. |
| 116 | | /// If the type is a pointer, this is the pointer address in virtual address space. |
| 119 | /// |
| 120 | /// If the type is a pointer, this is the pointer address in |
| 121 | /// virtual address space. |
| 117 | 122 | immediate: u64, |
| 118 | 123 | /// The value is in a target-specific register. |
| 119 | 124 | register: Register, |
| 125 | /// The value is a tuple { wrapped: u32, overflow: u1 } where |
| 126 | /// wrapped is stored in the register and the overflow bit is |
| 127 | /// stored in the C flag of the CPSR. |
| 128 | /// |
| 129 | /// This MCValue is only generated by a add_with_overflow or |
| 130 | /// sub_with_overflow instruction operating on u32. |
| 131 | register_c_flag: Register, |
| 132 | /// The value is a tuple { wrapped: i32, overflow: u1 } where |
| 133 | /// wrapped is stored in the register and the overflow bit is |
| 134 | /// stored in the V flag of the CPSR. |
| 135 | /// |
| 136 | /// This MCValue is only generated by a add_with_overflow or |
| 137 | /// sub_with_overflow instruction operating on i32. |
| 138 | register_v_flag: Register, |
| 120 | 139 | /// The value is in memory at a hard-coded address. |
| 121 | | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 140 | /// |
| 141 | /// If the type is a pointer, it means the pointer address is at |
| 142 | /// this memory location. |
| 122 | 143 | memory: u64, |
| 123 | | /// The value is in memory referenced indirectly via a GOT entry index. |
| 124 | | /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. |
| 125 | | /// When lowered, linker will emit relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and ARM64_RELOC_GOT_LOAD_PAGEOFF12. |
| 144 | /// The value is in memory referenced indirectly via a GOT entry |
| 145 | /// index. |
| 146 | /// |
| 147 | /// If the type is a pointer, it means the pointer is referenced |
| 148 | /// indirectly via GOT. When lowered, linker will emit |
| 149 | /// relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and |
| 150 | /// ARM64_RELOC_GOT_LOAD_PAGEOFF12. |
| 126 | 151 | got_load: u32, |
| 127 | 152 | /// The value is in memory referenced directly via symbol index. |
| 128 | | /// If the type is a pointer, it means the pointer is referenced directly via symbol index. |
| 129 | | /// When lowered, linker will emit a relocation of type ARM64_RELOC_PAGE21 and ARM64_RELOC_PAGEOFF12. |
| 153 | /// |
| 154 | /// If the type is a pointer, it means the pointer is referenced |
| 155 | /// directly via symbol index. When lowered, linker will emit a |
| 156 | /// relocation of type ARM64_RELOC_PAGE21 and |
| 157 | /// ARM64_RELOC_PAGEOFF12. |
| 130 | 158 | direct_load: u32, |
| 131 | 159 | /// The value is one of the stack variables. |
| 132 | | /// If the type is a pointer, it means the pointer address is in the stack at this offset. |
| 160 | /// |
| 161 | /// If the type is a pointer, it means the pointer address is in |
| 162 | /// the stack at this offset. |
| 133 | 163 | stack_offset: u32, |
| 134 | | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 164 | /// The value is a pointer to one of the stack variables (payload |
| 165 | /// is stack offset). |
| 135 | 166 | ptr_stack_offset: u32, |
| 136 | | /// The value is in the compare flags assuming an unsigned operation, |
| 137 | | /// with this operator applied on top of it. |
| 167 | /// The value is in the compare flags assuming an unsigned |
| 168 | /// operation, with this operator applied on top of it. |
| 138 | 169 | compare_flags_unsigned: math.CompareOperator, |
| 139 | 170 | /// The value is in the compare flags assuming a signed operation, |
| 140 | 171 | /// with this operator applied on top of it. |
| ... | ... | @@ -546,8 +577,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 546 | 577 | .trunc_float |
| 547 | 578 | => try self.airUnaryMath(inst), |
| 548 | 579 | |
| 549 | | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 550 | | .sub_with_overflow => try self.airSubWithOverflow(inst), |
| 580 | .add_with_overflow => try self.airOverflow(inst), |
| 581 | .sub_with_overflow => try self.airOverflow(inst), |
| 551 | 582 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 552 | 583 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 553 | 584 | |
| ... | ... | @@ -716,8 +747,13 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 716 | 747 | branch.inst_table.putAssumeCapacity(inst, .dead); |
| 717 | 748 | switch (prev_value) { |
| 718 | 749 | .register => |reg| { |
| 719 | | const canon_reg = toCanonicalReg(reg); |
| 720 | | self.register_manager.freeReg(canon_reg); |
| 750 | self.register_manager.freeReg(reg); |
| 751 | }, |
| 752 | .register_c_flag, |
| 753 | .register_v_flag, |
| 754 | => |reg| { |
| 755 | self.register_manager.freeReg(reg); |
| 756 | self.compare_flags_inst = null; |
| 721 | 757 | }, |
| 722 | 758 | .compare_flags_signed, .compare_flags_unsigned => { |
| 723 | 759 | self.compare_flags_inst = null; |
| ... | ... | @@ -857,7 +893,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 857 | 893 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 858 | 894 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 859 | 895 | const reg_mcv = self.getResolvedInstValue(inst); |
| 860 | | assert(reg == toCanonicalReg(reg_mcv.register)); |
| 896 | switch (reg_mcv) { |
| 897 | .register, |
| 898 | .register_c_flag, |
| 899 | .register_v_flag, |
| 900 | => |r| assert(reg.id() == r.id()), |
| 901 | else => unreachable, // not a register |
| 902 | } |
| 861 | 903 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 862 | 904 | try branch.inst_table.put(self.gpa, inst, stack_mcv); |
| 863 | 905 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); |
| ... | ... | @@ -868,7 +910,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 868 | 910 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 869 | 911 | if (self.compare_flags_inst) |inst_to_save| { |
| 870 | 912 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 871 | | assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); |
| 913 | switch (mcv) { |
| 914 | .compare_flags_signed, |
| 915 | .compare_flags_unsigned, |
| 916 | .register_c_flag, |
| 917 | .register_v_flag, |
| 918 | => {}, |
| 919 | else => unreachable, // mcv doesn't occupy the compare flags |
| 920 | } |
| 872 | 921 | |
| 873 | 922 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| 874 | 923 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| ... | ... | @@ -1245,25 +1294,33 @@ fn binOpRegister( |
| 1245 | 1294 | }; |
| 1246 | 1295 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 1247 | 1296 | |
| 1248 | | const dest_reg = if (maybe_inst) |inst| blk: { |
| 1249 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1297 | const dest_reg = switch (mir_tag) { |
| 1298 | .cmp_shifted_register => undefined, // cmp has no destination register |
| 1299 | else => if (maybe_inst) |inst| blk: { |
| 1300 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1250 | 1301 | |
| 1251 | | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| 1252 | | break :blk lhs_reg; |
| 1253 | | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| 1254 | | break :blk rhs_reg; |
| 1255 | | } else { |
| 1256 | | const raw_reg = try self.register_manager.allocReg(inst); |
| 1302 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| 1303 | break :blk lhs_reg; |
| 1304 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| 1305 | break :blk rhs_reg; |
| 1306 | } else { |
| 1307 | const raw_reg = try self.register_manager.allocReg(inst); |
| 1308 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1309 | } |
| 1310 | } else blk: { |
| 1311 | const raw_reg = try self.register_manager.allocReg(null); |
| 1257 | 1312 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1258 | | } |
| 1259 | | } else try self.register_manager.allocReg(null); |
| 1313 | }, |
| 1314 | }; |
| 1260 | 1315 | |
| 1261 | 1316 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1262 | 1317 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 1263 | 1318 | |
| 1264 | 1319 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1265 | 1320 | .add_shifted_register, |
| 1321 | .adds_shifted_register, |
| 1266 | 1322 | .sub_shifted_register, |
| 1323 | .subs_shifted_register, |
| 1267 | 1324 | => .{ .rrr_imm6_shift = .{ |
| 1268 | 1325 | .rd = dest_reg, |
| 1269 | 1326 | .rn = lhs_reg, |
| ... | ... | @@ -1286,6 +1343,13 @@ fn binOpRegister( |
| 1286 | 1343 | .rn = lhs_reg, |
| 1287 | 1344 | .rm = rhs_reg, |
| 1288 | 1345 | } }, |
| 1346 | .smull, |
| 1347 | .umull, |
| 1348 | => .{ .rrr = .{ |
| 1349 | .rd = dest_reg.to64(), |
| 1350 | .rn = lhs_reg, |
| 1351 | .rm = rhs_reg, |
| 1352 | } }, |
| 1289 | 1353 | .and_shifted_register, |
| 1290 | 1354 | .orr_shifted_register, |
| 1291 | 1355 | .eor_shifted_register, |
| ... | ... | @@ -1368,14 +1432,19 @@ fn binOpImmediate( |
| 1368 | 1432 | const raw_reg = try self.register_manager.allocReg(inst); |
| 1369 | 1433 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1370 | 1434 | } |
| 1371 | | } else try self.register_manager.allocReg(null), |
| 1435 | } else blk: { |
| 1436 | const raw_reg = try self.register_manager.allocReg(null); |
| 1437 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1438 | }, |
| 1372 | 1439 | }; |
| 1373 | 1440 | |
| 1374 | 1441 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1375 | 1442 | |
| 1376 | 1443 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1377 | 1444 | .add_immediate, |
| 1445 | .adds_immediate, |
| 1378 | 1446 | .sub_immediate, |
| 1447 | .subs_immediate, |
| 1379 | 1448 | => .{ .rr_imm12_sh = .{ |
| 1380 | 1449 | .rd = dest_reg, |
| 1381 | 1450 | .rn = lhs_reg, |
| ... | ... | @@ -1711,24 +1780,421 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1711 | 1780 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1712 | 1781 | } |
| 1713 | 1782 | |
| 1714 | | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1715 | | _ = inst; |
| 1716 | | return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); |
| 1717 | | } |
| 1783 | fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1784 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1785 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1786 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1787 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1788 | const lhs = try self.resolveInst(extra.lhs); |
| 1789 | const rhs = try self.resolveInst(extra.rhs); |
| 1790 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 1791 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 1792 | |
| 1793 | const tuple_ty = self.air.typeOfIndex(inst); |
| 1794 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| 1795 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| 1796 | const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 1797 | |
| 1798 | switch (lhs_ty.zigTypeTag()) { |
| 1799 | .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), |
| 1800 | .Int => { |
| 1801 | const mod = self.bin_file.options.module.?; |
| 1802 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1803 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1804 | switch (int_info.bits) { |
| 1805 | 1...31, 33...63 => { |
| 1806 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 1807 | |
| 1808 | try self.spillCompareFlagsIfOccupied(); |
| 1809 | self.compare_flags_inst = null; |
| 1810 | |
| 1811 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 1812 | .add_with_overflow => .add, |
| 1813 | .sub_with_overflow => .sub, |
| 1814 | else => unreachable, |
| 1815 | }; |
| 1816 | const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); |
| 1817 | const dest_reg = dest.register; |
| 1818 | self.register_manager.freezeRegs(&.{dest_reg}); |
| 1819 | defer self.register_manager.unfreezeRegs(&.{dest_reg}); |
| 1718 | 1820 | |
| 1719 | | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1720 | | _ = inst; |
| 1721 | | return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); |
| 1821 | const raw_truncated_reg = try self.register_manager.allocReg(null); |
| 1822 | const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); |
| 1823 | self.register_manager.freezeRegs(&.{truncated_reg}); |
| 1824 | defer self.register_manager.unfreezeRegs(&.{truncated_reg}); |
| 1825 | |
| 1826 | // sbfx/ubfx truncated, dest, #0, #bits |
| 1827 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 1828 | |
| 1829 | // cmp dest, truncated |
| 1830 | _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); |
| 1831 | |
| 1832 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1833 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); |
| 1834 | |
| 1835 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1836 | }, |
| 1837 | 32, 64 => { |
| 1838 | // Only say yes if the operation is |
| 1839 | // commutative, i.e. we can swap both of the |
| 1840 | // operands |
| 1841 | const lhs_immediate_ok = switch (tag) { |
| 1842 | .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 1843 | .sub_with_overflow => false, |
| 1844 | else => unreachable, |
| 1845 | }; |
| 1846 | const rhs_immediate_ok = switch (tag) { |
| 1847 | .add_with_overflow, |
| 1848 | .sub_with_overflow, |
| 1849 | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 1850 | else => unreachable, |
| 1851 | }; |
| 1852 | |
| 1853 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 1854 | .add_with_overflow => .adds_shifted_register, |
| 1855 | .sub_with_overflow => .subs_shifted_register, |
| 1856 | else => unreachable, |
| 1857 | }; |
| 1858 | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { |
| 1859 | .add_with_overflow => .adds_immediate, |
| 1860 | .sub_with_overflow => .subs_immediate, |
| 1861 | else => unreachable, |
| 1862 | }; |
| 1863 | |
| 1864 | try self.spillCompareFlagsIfOccupied(); |
| 1865 | self.compare_flags_inst = inst; |
| 1866 | |
| 1867 | const dest = blk: { |
| 1868 | if (rhs_immediate_ok) { |
| 1869 | break :blk try self.binOpImmediate(mir_tag_immediate, null, lhs, rhs, lhs_ty, false); |
| 1870 | } else if (lhs_immediate_ok) { |
| 1871 | // swap lhs and rhs |
| 1872 | break :blk try self.binOpImmediate(mir_tag_immediate, null, rhs, lhs, rhs_ty, true); |
| 1873 | } else { |
| 1874 | break :blk try self.binOpRegister(mir_tag_register, null, lhs, rhs, lhs_ty, rhs_ty); |
| 1875 | } |
| 1876 | }; |
| 1877 | |
| 1878 | switch (int_info.signedness) { |
| 1879 | .unsigned => break :result MCValue{ .register_c_flag = dest.register }, |
| 1880 | .signed => break :result MCValue{ .register_v_flag = dest.register }, |
| 1881 | } |
| 1882 | }, |
| 1883 | else => return self.fail("TODO overflow operations on integers > u32/i32", .{}), |
| 1884 | } |
| 1885 | }, |
| 1886 | else => unreachable, |
| 1887 | } |
| 1888 | }; |
| 1889 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1722 | 1890 | } |
| 1723 | 1891 | |
| 1724 | 1892 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1725 | | _ = inst; |
| 1726 | | return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); |
| 1893 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1894 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1895 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 1896 | const result: MCValue = result: { |
| 1897 | const lhs = try self.resolveInst(extra.lhs); |
| 1898 | const rhs = try self.resolveInst(extra.rhs); |
| 1899 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 1900 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 1901 | |
| 1902 | const tuple_ty = self.air.typeOfIndex(inst); |
| 1903 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| 1904 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| 1905 | const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 1906 | |
| 1907 | switch (lhs_ty.zigTypeTag()) { |
| 1908 | .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), |
| 1909 | .Int => { |
| 1910 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1911 | |
| 1912 | if (int_info.bits <= 32) { |
| 1913 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 1914 | |
| 1915 | try self.spillCompareFlagsIfOccupied(); |
| 1916 | self.compare_flags_inst = null; |
| 1917 | |
| 1918 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { |
| 1919 | .signed => .smull, |
| 1920 | .unsigned => .umull, |
| 1921 | }; |
| 1922 | |
| 1923 | const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); |
| 1924 | const dest_reg = dest.register; |
| 1925 | self.register_manager.freezeRegs(&.{dest_reg}); |
| 1926 | defer self.register_manager.unfreezeRegs(&.{dest_reg}); |
| 1927 | |
| 1928 | const truncated_reg = try self.register_manager.allocReg(null); |
| 1929 | self.register_manager.freezeRegs(&.{truncated_reg}); |
| 1930 | defer self.register_manager.unfreezeRegs(&.{truncated_reg}); |
| 1931 | |
| 1932 | try self.truncRegister( |
| 1933 | dest_reg.to32(), |
| 1934 | truncated_reg.to32(), |
| 1935 | int_info.signedness, |
| 1936 | int_info.bits, |
| 1937 | ); |
| 1938 | |
| 1939 | switch (int_info.signedness) { |
| 1940 | .signed => { |
| 1941 | _ = try self.addInst(.{ |
| 1942 | .tag = .cmp_extended_register, |
| 1943 | .data = .{ .rr_extend_shift = .{ |
| 1944 | .rn = dest_reg.to64(), |
| 1945 | .rm = truncated_reg.to32(), |
| 1946 | .ext_type = .sxtw, |
| 1947 | .imm3 = 0, |
| 1948 | } }, |
| 1949 | }); |
| 1950 | }, |
| 1951 | .unsigned => { |
| 1952 | _ = try self.addInst(.{ |
| 1953 | .tag = .cmp_extended_register, |
| 1954 | .data = .{ .rr_extend_shift = .{ |
| 1955 | .rn = dest_reg.to64(), |
| 1956 | .rm = truncated_reg.to32(), |
| 1957 | .ext_type = .uxtw, |
| 1958 | .imm3 = 0, |
| 1959 | } }, |
| 1960 | }); |
| 1961 | }, |
| 1962 | } |
| 1963 | |
| 1964 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1965 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| 1966 | .compare_flags_unsigned = .neq, |
| 1967 | }); |
| 1968 | |
| 1969 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1970 | } else if (int_info.bits <= 64) { |
| 1971 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 1972 | |
| 1973 | try self.spillCompareFlagsIfOccupied(); |
| 1974 | self.compare_flags_inst = null; |
| 1975 | |
| 1976 | // TODO this should really be put in a helper similar to `binOpRegister` |
| 1977 | const lhs_is_register = lhs == .register; |
| 1978 | const rhs_is_register = rhs == .register; |
| 1979 | |
| 1980 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 1981 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| 1982 | |
| 1983 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 1984 | const raw_reg = try self.register_manager.allocReg(null); |
| 1985 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1986 | self.register_manager.freezeRegs(&.{reg}); |
| 1987 | break :blk reg; |
| 1988 | }; |
| 1989 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 1990 | |
| 1991 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 1992 | const raw_reg = try self.register_manager.allocReg(null); |
| 1993 | const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); |
| 1994 | self.register_manager.freezeRegs(&.{reg}); |
| 1995 | break :blk reg; |
| 1996 | }; |
| 1997 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 1998 | |
| 1999 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2000 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2001 | |
| 2002 | // TODO reuse operands |
| 2003 | const dest_reg = blk: { |
| 2004 | const raw_reg = try self.register_manager.allocReg(null); |
| 2005 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 2006 | self.register_manager.freezeRegs(&.{reg}); |
| 2007 | break :blk reg; |
| 2008 | }; |
| 2009 | defer self.register_manager.unfreezeRegs(&.{dest_reg}); |
| 2010 | |
| 2011 | switch (int_info.signedness) { |
| 2012 | .signed => { |
| 2013 | // mul dest, lhs, rhs |
| 2014 | _ = try self.addInst(.{ |
| 2015 | .tag = .mul, |
| 2016 | .data = .{ .rrr = .{ |
| 2017 | .rd = dest_reg, |
| 2018 | .rn = lhs_reg, |
| 2019 | .rm = rhs_reg, |
| 2020 | } }, |
| 2021 | }); |
| 2022 | |
| 2023 | const dest_high_reg = try self.register_manager.allocReg(null); |
| 2024 | self.register_manager.freezeRegs(&.{dest_high_reg}); |
| 2025 | defer self.register_manager.unfreezeRegs(&.{dest_high_reg}); |
| 2026 | |
| 2027 | // smulh dest_high, lhs, rhs |
| 2028 | _ = try self.addInst(.{ |
| 2029 | .tag = .smulh, |
| 2030 | .data = .{ .rrr = .{ |
| 2031 | .rd = dest_high_reg, |
| 2032 | .rn = lhs_reg, |
| 2033 | .rm = rhs_reg, |
| 2034 | } }, |
| 2035 | }); |
| 2036 | |
| 2037 | // cmp dest_high, dest, asr #63 |
| 2038 | _ = try self.addInst(.{ |
| 2039 | .tag = .cmp_shifted_register, |
| 2040 | .data = .{ .rr_imm6_shift = .{ |
| 2041 | .rn = dest_high_reg, |
| 2042 | .rm = dest_reg, |
| 2043 | .imm6 = 63, |
| 2044 | .shift = .asr, |
| 2045 | } }, |
| 2046 | }); |
| 2047 | |
| 2048 | const shift: u6 = @intCast(u6, @as(u7, 64) - @intCast(u7, int_info.bits)); |
| 2049 | if (shift > 0) { |
| 2050 | // lsl dest_high, dest, #shift |
| 2051 | _ = try self.addInst(.{ |
| 2052 | .tag = .lsl_immediate, |
| 2053 | .data = .{ .rr_shift = .{ |
| 2054 | .rd = dest_high_reg, |
| 2055 | .rn = dest_reg, |
| 2056 | .shift = shift, |
| 2057 | } }, |
| 2058 | }); |
| 2059 | |
| 2060 | // cmp dest, dest_high, #shift |
| 2061 | _ = try self.addInst(.{ |
| 2062 | .tag = .cmp_shifted_register, |
| 2063 | .data = .{ .rr_imm6_shift = .{ |
| 2064 | .rn = dest_reg, |
| 2065 | .rm = dest_high_reg, |
| 2066 | .imm6 = shift, |
| 2067 | .shift = .asr, |
| 2068 | } }, |
| 2069 | }); |
| 2070 | } |
| 2071 | }, |
| 2072 | .unsigned => { |
| 2073 | const dest_high_reg = try self.register_manager.allocReg(null); |
| 2074 | self.register_manager.freezeRegs(&.{dest_high_reg}); |
| 2075 | defer self.register_manager.unfreezeRegs(&.{dest_high_reg}); |
| 2076 | |
| 2077 | // umulh dest_high, lhs, rhs |
| 2078 | _ = try self.addInst(.{ |
| 2079 | .tag = .umulh, |
| 2080 | .data = .{ .rrr = .{ |
| 2081 | .rd = dest_high_reg, |
| 2082 | .rn = lhs_reg, |
| 2083 | .rm = rhs_reg, |
| 2084 | } }, |
| 2085 | }); |
| 2086 | |
| 2087 | // mul dest, lhs, rhs |
| 2088 | _ = try self.addInst(.{ |
| 2089 | .tag = .mul, |
| 2090 | .data = .{ .rrr = .{ |
| 2091 | .rd = dest_reg, |
| 2092 | .rn = lhs_reg, |
| 2093 | .rm = rhs_reg, |
| 2094 | } }, |
| 2095 | }); |
| 2096 | |
| 2097 | _ = try self.binOp( |
| 2098 | .cmp_eq, |
| 2099 | null, |
| 2100 | .{ .register = dest_high_reg }, |
| 2101 | .{ .immediate = 0 }, |
| 2102 | Type.usize, |
| 2103 | Type.usize, |
| 2104 | ); |
| 2105 | |
| 2106 | if (int_info.bits < 64) { |
| 2107 | // lsr dest_high, dest, #shift |
| 2108 | _ = try self.addInst(.{ |
| 2109 | .tag = .lsr_immediate, |
| 2110 | .data = .{ .rr_shift = .{ |
| 2111 | .rd = dest_high_reg, |
| 2112 | .rn = dest_reg, |
| 2113 | .shift = @intCast(u6, int_info.bits), |
| 2114 | } }, |
| 2115 | }); |
| 2116 | |
| 2117 | _ = try self.binOp( |
| 2118 | .cmp_eq, |
| 2119 | null, |
| 2120 | .{ .register = dest_high_reg }, |
| 2121 | .{ .immediate = 0 }, |
| 2122 | Type.usize, |
| 2123 | Type.usize, |
| 2124 | ); |
| 2125 | } |
| 2126 | }, |
| 2127 | } |
| 2128 | |
| 2129 | const truncated_reg = try self.register_manager.allocReg(null); |
| 2130 | self.register_manager.freezeRegs(&.{truncated_reg}); |
| 2131 | defer self.register_manager.unfreezeRegs(&.{truncated_reg}); |
| 2132 | |
| 2133 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 2134 | |
| 2135 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 2136 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| 2137 | .compare_flags_unsigned = .neq, |
| 2138 | }); |
| 2139 | |
| 2140 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2141 | } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{}); |
| 2142 | }, |
| 2143 | else => unreachable, |
| 2144 | } |
| 2145 | }; |
| 2146 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1727 | 2147 | } |
| 1728 | 2148 | |
| 1729 | 2149 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1730 | | _ = inst; |
| 1731 | | return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); |
| 2150 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2151 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2152 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 2153 | const result: MCValue = result: { |
| 2154 | const lhs = try self.resolveInst(extra.lhs); |
| 2155 | const rhs = try self.resolveInst(extra.rhs); |
| 2156 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2157 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 2158 | |
| 2159 | const tuple_ty = self.air.typeOfIndex(inst); |
| 2160 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| 2161 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| 2162 | const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); |
| 2163 | |
| 2164 | switch (lhs_ty.zigTypeTag()) { |
| 2165 | .Vector => return self.fail("TODO implement shl_with_overflow for vectors", .{}), |
| 2166 | .Int => { |
| 2167 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2168 | if (int_info.bits <= 64) { |
| 2169 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 2170 | |
| 2171 | if (lhs == .register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 2172 | defer if (lhs == .register) self.register_manager.unfreezeRegs(&.{lhs.register}); |
| 2173 | |
| 2174 | try self.spillCompareFlagsIfOccupied(); |
| 2175 | self.compare_flags_inst = null; |
| 2176 | |
| 2177 | // lsl dest, lhs, rhs |
| 2178 | const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); |
| 2179 | |
| 2180 | // asr/lsr reconstructed, dest, rhs |
| 2181 | const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); |
| 2182 | |
| 2183 | // cmp lhs, reconstructed |
| 2184 | _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty); |
| 2185 | |
| 2186 | try self.genSetStack(lhs_ty, stack_offset, dest); |
| 2187 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); |
| 2188 | |
| 2189 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2190 | } else { |
| 2191 | return self.fail("TODO overflow operations on integers > u64/i64", .{}); |
| 2192 | } |
| 2193 | }, |
| 2194 | else => unreachable, |
| 2195 | } |
| 2196 | }; |
| 2197 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1732 | 2198 | } |
| 1733 | 2199 | |
| 1734 | 2200 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1957,7 +2423,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1957 | 2423 | switch (elem_size) { |
| 1958 | 2424 | else => { |
| 1959 | 2425 | const dest = try self.allocRegOrMem(inst, true); |
| 1960 | | const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ty, Type.usize); |
| 2426 | const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); |
| 1961 | 2427 | try self.load(dest, addr, slice_ptr_field_type); |
| 1962 | 2428 | |
| 1963 | 2429 | break :result dest; |
| ... | ... | @@ -2085,8 +2551,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2085 | 2551 | .undef => unreachable, |
| 2086 | 2552 | .unreach => unreachable, |
| 2087 | 2553 | .dead => unreachable, |
| 2088 | | .compare_flags_unsigned => unreachable, |
| 2089 | | .compare_flags_signed => unreachable, |
| 2554 | .compare_flags_unsigned, |
| 2555 | .compare_flags_signed, |
| 2556 | .register_c_flag, |
| 2557 | .register_v_flag, |
| 2558 | => unreachable, // cannot hold an address |
| 2090 | 2559 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 2091 | 2560 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), |
| 2092 | 2561 | .register => |addr_reg| { |
| ... | ... | @@ -2303,8 +2772,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2303 | 2772 | .undef => unreachable, |
| 2304 | 2773 | .unreach => unreachable, |
| 2305 | 2774 | .dead => unreachable, |
| 2306 | | .compare_flags_unsigned => unreachable, |
| 2307 | | .compare_flags_signed => unreachable, |
| 2775 | .compare_flags_unsigned, |
| 2776 | .compare_flags_signed, |
| 2777 | .register_c_flag, |
| 2778 | .register_v_flag, |
| 2779 | => unreachable, // cannot hold an address |
| 2308 | 2780 | .immediate => |imm| { |
| 2309 | 2781 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); |
| 2310 | 2782 | }, |
| ... | ... | @@ -2409,9 +2881,60 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 2409 | 2881 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2410 | 2882 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2411 | 2883 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 2412 | | _ = extra; |
| 2413 | | return self.fail("TODO implement codegen struct_field_val", .{}); |
| 2414 | | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); |
| 2884 | const operand = extra.struct_operand; |
| 2885 | const index = extra.field_index; |
| 2886 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2887 | const mcv = try self.resolveInst(operand); |
| 2888 | const struct_ty = self.air.typeOf(operand); |
| 2889 | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); |
| 2890 | |
| 2891 | switch (mcv) { |
| 2892 | .dead, .unreach => unreachable, |
| 2893 | .stack_offset => |off| { |
| 2894 | break :result MCValue{ .stack_offset = off - struct_field_offset }; |
| 2895 | }, |
| 2896 | .memory => |addr| { |
| 2897 | break :result MCValue{ .memory = addr + struct_field_offset }; |
| 2898 | }, |
| 2899 | .register_c_flag, |
| 2900 | .register_v_flag, |
| 2901 | => |reg| { |
| 2902 | switch (index) { |
| 2903 | 0 => { |
| 2904 | // get wrapped value: return register |
| 2905 | break :result MCValue{ .register = reg }; |
| 2906 | }, |
| 2907 | 1 => { |
| 2908 | // TODO return special MCValue condition flags |
| 2909 | // get overflow bit: set register to C flag |
| 2910 | // resp. V flag |
| 2911 | const raw_dest_reg = try self.register_manager.allocReg(null); |
| 2912 | const dest_reg = raw_dest_reg.to32(); |
| 2913 | |
| 2914 | // C flag: cset reg, cs |
| 2915 | // V flag: cset reg, vs |
| 2916 | _ = try self.addInst(.{ |
| 2917 | .tag = .cset, |
| 2918 | .data = .{ .r_cond = .{ |
| 2919 | .rd = dest_reg, |
| 2920 | .cond = switch (mcv) { |
| 2921 | .register_c_flag => .cs, |
| 2922 | .register_v_flag => .vs, |
| 2923 | else => unreachable, |
| 2924 | }, |
| 2925 | } }, |
| 2926 | }); |
| 2927 | |
| 2928 | break :result MCValue{ .register = dest_reg }; |
| 2929 | }, |
| 2930 | else => unreachable, |
| 2931 | } |
| 2932 | }, |
| 2933 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), |
| 2934 | } |
| 2935 | }; |
| 2936 | |
| 2937 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 2415 | 2938 | } |
| 2416 | 2939 | |
| 2417 | 2940 | fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2451,7 +2974,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2451 | 2974 | |
| 2452 | 2975 | switch (mcv) { |
| 2453 | 2976 | .register => |reg| { |
| 2454 | | self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst); |
| 2977 | self.register_manager.getRegAssumeFree(reg, inst); |
| 2455 | 2978 | }, |
| 2456 | 2979 | else => {}, |
| 2457 | 2980 | } |
| ... | ... | @@ -2516,15 +3039,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2516 | 3039 | |
| 2517 | 3040 | switch (mc_arg) { |
| 2518 | 3041 | .none => continue, |
| 2519 | | .undef => unreachable, |
| 2520 | | .immediate => unreachable, |
| 2521 | | .unreach => unreachable, |
| 2522 | | .dead => unreachable, |
| 2523 | | .memory => unreachable, |
| 2524 | | .compare_flags_signed => unreachable, |
| 2525 | | .compare_flags_unsigned => unreachable, |
| 2526 | | .got_load => unreachable, |
| 2527 | | .direct_load => unreachable, |
| 2528 | 3042 | .register => |reg| { |
| 2529 | 3043 | try self.register_manager.getReg(reg, null); |
| 2530 | 3044 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| ... | ... | @@ -2535,6 +3049,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2535 | 3049 | .ptr_stack_offset => { |
| 2536 | 3050 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| 2537 | 3051 | }, |
| 3052 | else => unreachable, |
| 2538 | 3053 | } |
| 2539 | 3054 | } |
| 2540 | 3055 | |
| ... | ... | @@ -3438,6 +3953,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3438 | 3953 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 3439 | 3954 | } |
| 3440 | 3955 | }, |
| 3956 | .register_c_flag, |
| 3957 | .register_v_flag, |
| 3958 | => { |
| 3959 | return self.fail("TODO implement genSetStack {}", .{mcv}); |
| 3960 | }, |
| 3441 | 3961 | .got_load, |
| 3442 | 3962 | .direct_load, |
| 3443 | 3963 | .memory, |
| ... | ... | @@ -3555,7 +4075,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3555 | 4075 | .tag = .cset, |
| 3556 | 4076 | .data = .{ .r_cond = .{ |
| 3557 | 4077 | .rd = reg, |
| 3558 | | .cond = condition.negate(), |
| 4078 | .cond = condition, |
| 3559 | 4079 | } }, |
| 3560 | 4080 | }); |
| 3561 | 4081 | }, |
| ... | ... | @@ -3598,6 +4118,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3598 | 4118 | .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, |
| 3599 | 4119 | }); |
| 3600 | 4120 | }, |
| 4121 | .register_c_flag, |
| 4122 | .register_v_flag, |
| 4123 | => unreachable, // doesn't fit into a register |
| 3601 | 4124 | .got_load, |
| 3602 | 4125 | .direct_load, |
| 3603 | 4126 | => |sym_index| { |
| ... | ... | @@ -4199,8 +4722,3 @@ fn registerAlias(reg: Register, size_bytes: u64) Register { |
| 4199 | 4722 | unreachable; // TODO handle floating-point registers |
| 4200 | 4723 | } |
| 4201 | 4724 | } |
| 4202 | | |
| 4203 | | /// Resolves any aliased registers to the 64-bit wide ones. |
| 4204 | | fn toCanonicalReg(reg: Register) Register { |
| 4205 | | return reg.to64(); |
| 4206 | | } |