| ... | @@ -102,9 +102,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, | ... | @@ -102,9 +102,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 102 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | 102 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 103 | | 103 | |
| 104 | const MCValue = union(enum) { | 104 | const MCValue = union(enum) { |
| 105 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. | 105 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 |
| 106 | /// TODO Look into deleting this tag and using `dead` instead, since every use | 106 | /// tag, etc. |
| 107 | /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. | 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 | none, | 111 | none, |
| 109 | /// Control flow will not allow this value to be observed. | 112 | /// Control flow will not allow this value to be observed. |
| 110 | unreach, | 113 | unreach, |
| ... | @@ -113,28 +116,56 @@ const MCValue = union(enum) { | ... | @@ -113,28 +116,56 @@ const MCValue = union(enum) { |
| 113 | /// The value is undefined. | 116 | /// The value is undefined. |
| 114 | undef, | 117 | undef, |
| 115 | /// A pointer-sized integer that fits in a register. | 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 | immediate: u64, | 122 | immediate: u64, |
| 118 | /// The value is in a target-specific register. | 123 | /// The value is in a target-specific register. |
| 119 | register: Register, | 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 | /// The value is in memory at a hard-coded address. | 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 | memory: u64, | 143 | memory: u64, |
| 123 | /// The value is in memory referenced indirectly via a GOT entry index. | 144 | /// The value is in memory referenced indirectly via a GOT entry |
| 124 | /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. | 145 | /// index. |
| 125 | /// When lowered, linker will emit relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and ARM64_RELOC_GOT_LOAD_PAGEOFF12. | 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 | got_load: u32, | 151 | got_load: u32, |
| 127 | /// The value is in memory referenced directly via symbol index. | 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. | 153 | /// |
| 129 | /// When lowered, linker will emit a relocation of type ARM64_RELOC_PAGE21 and ARM64_RELOC_PAGEOFF12. | 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 | direct_load: u32, | 158 | direct_load: u32, |
| 131 | /// The value is one of the stack variables. | 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 | stack_offset: u32, | 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 | ptr_stack_offset: u32, | 166 | ptr_stack_offset: u32, |
| 136 | /// The value is in the compare flags assuming an unsigned operation, | 167 | /// The value is in the compare flags assuming an unsigned |
| 137 | /// with this operator applied on top of it. | 168 | /// operation, with this operator applied on top of it. |
| 138 | compare_flags_unsigned: math.CompareOperator, | 169 | compare_flags_unsigned: math.CompareOperator, |
| 139 | /// The value is in the compare flags assuming a signed operation, | 170 | /// The value is in the compare flags assuming a signed operation, |
| 140 | /// with this operator applied on top of it. | 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,8 +577,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 546 | .trunc_float | 577 | .trunc_float |
| 547 | => try self.airUnaryMath(inst), | 578 | => try self.airUnaryMath(inst), |
| 548 | | 579 | |
| 549 | .add_with_overflow => try self.airAddWithOverflow(inst), | 580 | .add_with_overflow => try self.airOverflow(inst), |
| 550 | .sub_with_overflow => try self.airSubWithOverflow(inst), | 581 | .sub_with_overflow => try self.airOverflow(inst), |
| 551 | .mul_with_overflow => try self.airMulWithOverflow(inst), | 582 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 552 | .shl_with_overflow => try self.airShlWithOverflow(inst), | 583 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 553 | | 584 | |
| ... | @@ -716,8 +747,13 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { | ... | @@ -716,8 +747,13 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 716 | branch.inst_table.putAssumeCapacity(inst, .dead); | 747 | branch.inst_table.putAssumeCapacity(inst, .dead); |
| 717 | switch (prev_value) { | 748 | switch (prev_value) { |
| 718 | .register => |reg| { | 749 | .register => |reg| { |
| 719 | const canon_reg = toCanonicalReg(reg); | 750 | self.register_manager.freeReg(reg); |
| 720 | self.register_manager.freeReg(canon_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 | .compare_flags_signed, .compare_flags_unsigned => { | 758 | .compare_flags_signed, .compare_flags_unsigned => { |
| 723 | self.compare_flags_inst = null; | 759 | self.compare_flags_inst = null; |
| ... | @@ -857,7 +893,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void | ... | @@ -857,7 +893,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 857 | const stack_mcv = try self.allocRegOrMem(inst, false); | 893 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 858 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); | 894 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 859 | const reg_mcv = self.getResolvedInstValue(inst); | 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 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 903 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 862 | try branch.inst_table.put(self.gpa, inst, stack_mcv); | 904 | try branch.inst_table.put(self.gpa, inst, stack_mcv); |
| 863 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); | 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,7 +910,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 868 | fn spillCompareFlagsIfOccupied(self: *Self) !void { | 910 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 869 | if (self.compare_flags_inst) |inst_to_save| { | 911 | if (self.compare_flags_inst) |inst_to_save| { |
| 870 | const mcv = self.getResolvedInstValue(inst_to_save); | 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 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); | 922 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| 874 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); | 923 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| ... | @@ -1245,25 +1294,33 @@ fn binOpRegister( | ... | @@ -1245,25 +1294,33 @@ fn binOpRegister( |
| 1245 | }; | 1294 | }; |
| 1246 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); | 1295 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 1247 | | 1296 | |
| 1248 | const dest_reg = if (maybe_inst) |inst| blk: { | 1297 | const dest_reg = switch (mir_tag) { |
| 1249 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 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)) { | 1302 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| 1252 | break :blk lhs_reg; | 1303 | break :blk lhs_reg; |
| 1253 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { | 1304 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| 1254 | break :blk rhs_reg; | 1305 | break :blk rhs_reg; |
| 1255 | } else { | 1306 | } else { |
| 1256 | const raw_reg = try self.register_manager.allocReg(inst); | 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 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | 1312 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1258 | } | 1313 | }, |
| 1259 | } else try self.register_manager.allocReg(null); | 1314 | }; |
| 1260 | | 1315 | |
| 1261 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 1316 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1262 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | 1317 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 1263 | | 1318 | |
| 1264 | const mir_data: Mir.Inst.Data = switch (mir_tag) { | 1319 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1265 | .add_shifted_register, | 1320 | .add_shifted_register, |
| | 1321 | .adds_shifted_register, |
| 1266 | .sub_shifted_register, | 1322 | .sub_shifted_register, |
| | 1323 | .subs_shifted_register, |
| 1267 | => .{ .rrr_imm6_shift = .{ | 1324 | => .{ .rrr_imm6_shift = .{ |
| 1268 | .rd = dest_reg, | 1325 | .rd = dest_reg, |
| 1269 | .rn = lhs_reg, | 1326 | .rn = lhs_reg, |
| ... | @@ -1286,6 +1343,13 @@ fn binOpRegister( | ... | @@ -1286,6 +1343,13 @@ fn binOpRegister( |
| 1286 | .rn = lhs_reg, | 1343 | .rn = lhs_reg, |
| 1287 | .rm = rhs_reg, | 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 | .and_shifted_register, | 1353 | .and_shifted_register, |
| 1290 | .orr_shifted_register, | 1354 | .orr_shifted_register, |
| 1291 | .eor_shifted_register, | 1355 | .eor_shifted_register, |
| ... | @@ -1368,14 +1432,19 @@ fn binOpImmediate( | ... | @@ -1368,14 +1432,19 @@ fn binOpImmediate( |
| 1368 | const raw_reg = try self.register_manager.allocReg(inst); | 1432 | const raw_reg = try self.register_manager.allocReg(inst); |
| 1369 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | 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 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 1441 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1375 | | 1442 | |
| 1376 | const mir_data: Mir.Inst.Data = switch (mir_tag) { | 1443 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1377 | .add_immediate, | 1444 | .add_immediate, |
| | 1445 | .adds_immediate, |
| 1378 | .sub_immediate, | 1446 | .sub_immediate, |
| | 1447 | .subs_immediate, |
| 1379 | => .{ .rr_imm12_sh = .{ | 1448 | => .{ .rr_imm12_sh = .{ |
| 1380 | .rd = dest_reg, | 1449 | .rd = dest_reg, |
| 1381 | .rn = lhs_reg, | 1450 | .rn = lhs_reg, |
| ... | @@ -1711,24 +1780,421 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1711,24 +1780,421 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1711 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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 { | 1783 | fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1715 | _ = inst; | 1784 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1716 | return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); | 1785 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1717 | } | 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 { | 1821 | const raw_truncated_reg = try self.register_manager.allocReg(null); |
| 1720 | _ = inst; | 1822 | const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); |
| 1721 | return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); | 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 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1892 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1725 | _ = inst; | 1893 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1726 | return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); | 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 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 2149 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1730 | _ = inst; | 2150 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1731 | return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); | 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 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { | 2200 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -1957,7 +2423,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1957,7 +2423,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1957 | switch (elem_size) { | 2423 | switch (elem_size) { |
| 1958 | else => { | 2424 | else => { |
| 1959 | const dest = try self.allocRegOrMem(inst, true); | 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 | try self.load(dest, addr, slice_ptr_field_type); | 2427 | try self.load(dest, addr, slice_ptr_field_type); |
| 1962 | | 2428 | |
| 1963 | break :result dest; | 2429 | break :result dest; |
| ... | @@ -2085,8 +2551,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -2085,8 +2551,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2085 | .undef => unreachable, | 2551 | .undef => unreachable, |
| 2086 | .unreach => unreachable, | 2552 | .unreach => unreachable, |
| 2087 | .dead => unreachable, | 2553 | .dead => unreachable, |
| 2088 | .compare_flags_unsigned => unreachable, | 2554 | .compare_flags_unsigned, |
| 2089 | .compare_flags_signed => unreachable, | 2555 | .compare_flags_signed, |
| | 2556 | .register_c_flag, |
| | 2557 | .register_v_flag, |
| | 2558 | => unreachable, // cannot hold an address |
| 2090 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), | 2559 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 2091 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), | 2560 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), |
| 2092 | .register => |addr_reg| { | 2561 | .register => |addr_reg| { |
| ... | @@ -2303,8 +2772,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -2303,8 +2772,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2303 | .undef => unreachable, | 2772 | .undef => unreachable, |
| 2304 | .unreach => unreachable, | 2773 | .unreach => unreachable, |
| 2305 | .dead => unreachable, | 2774 | .dead => unreachable, |
| 2306 | .compare_flags_unsigned => unreachable, | 2775 | .compare_flags_unsigned, |
| 2307 | .compare_flags_signed => unreachable, | 2776 | .compare_flags_signed, |
| | 2777 | .register_c_flag, |
| | 2778 | .register_v_flag, |
| | 2779 | => unreachable, // cannot hold an address |
| 2308 | .immediate => |imm| { | 2780 | .immediate => |imm| { |
| 2309 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); | 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,9 +2881,60 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 2409 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | 2881 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2410 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 2882 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2411 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; | 2883 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 2412 | _ = extra; | 2884 | const operand = extra.struct_operand; |
| 2413 | return self.fail("TODO implement codegen struct_field_val", .{}); | 2885 | const index = extra.field_index; |
| 2414 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); | 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 | fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { | 2940 | fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -2451,7 +2974,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2451,7 +2974,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2451 | | 2974 | |
| 2452 | switch (mcv) { | 2975 | switch (mcv) { |
| 2453 | .register => |reg| { | 2976 | .register => |reg| { |
| 2454 | self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst); | 2977 | self.register_manager.getRegAssumeFree(reg, inst); |
| 2455 | }, | 2978 | }, |
| 2456 | else => {}, | 2979 | else => {}, |
| 2457 | } | 2980 | } |
| ... | @@ -2516,15 +3039,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2516,15 +3039,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2516 | | 3039 | |
| 2517 | switch (mc_arg) { | 3040 | switch (mc_arg) { |
| 2518 | .none => continue, | 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 | .register => |reg| { | 3042 | .register => |reg| { |
| 2529 | try self.register_manager.getReg(reg, null); | 3043 | try self.register_manager.getReg(reg, null); |
| 2530 | try self.genSetReg(arg_ty, reg, arg_mcv); | 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,6 +3049,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2535 | .ptr_stack_offset => { | 3049 | .ptr_stack_offset => { |
| 2536 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); | 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,6 +3953,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3438 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), | 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 | .got_load, | 3961 | .got_load, |
| 3442 | .direct_load, | 3962 | .direct_load, |
| 3443 | .memory, | 3963 | .memory, |
| ... | @@ -3555,7 +4075,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -3555,7 +4075,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3555 | .tag = .cset, | 4075 | .tag = .cset, |
| 3556 | .data = .{ .r_cond = .{ | 4076 | .data = .{ .r_cond = .{ |
| 3557 | .rd = reg, | 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,6 +4118,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3598 | .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, | 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 | .got_load, | 4124 | .got_load, |
| 3602 | .direct_load, | 4125 | .direct_load, |
| 3603 | => |sym_index| { | 4126 | => |sym_index| { |
| ... | @@ -4199,8 +4722,3 @@ fn registerAlias(reg: Register, size_bytes: u64) Register { | ... | @@ -4199,8 +4722,3 @@ fn registerAlias(reg: Register, size_bytes: u64) Register { |
| 4199 | unreachable; // TODO handle floating-point registers | 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 | } | | |