| ... | ... | @@ -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. |
| ... | ... | @@ -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); |
| ... | ... | @@ -1269,7 +1318,9 @@ fn binOpRegister( |
| 1269 | 1318 | |
| 1270 | 1319 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1271 | 1320 | .add_shifted_register, |
| 1321 | .adds_shifted_register, |
| 1272 | 1322 | .sub_shifted_register, |
| 1323 | .subs_shifted_register, |
| 1273 | 1324 | => .{ .rrr_imm6_shift = .{ |
| 1274 | 1325 | .rd = dest_reg, |
| 1275 | 1326 | .rn = lhs_reg, |
| ... | ... | @@ -1384,7 +1435,9 @@ fn binOpImmediate( |
| 1384 | 1435 | |
| 1385 | 1436 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1386 | 1437 | .add_immediate, |
| 1438 | .adds_immediate, |
| 1387 | 1439 | .sub_immediate, |
| 1440 | .subs_immediate, |
| 1388 | 1441 | => .{ .rr_imm12_sh = .{ |
| 1389 | 1442 | .rd = dest_reg, |
| 1390 | 1443 | .rn = lhs_reg, |
| ... | ... | @@ -1774,7 +1827,52 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1774 | 1827 | |
| 1775 | 1828 | break :result MCValue{ .stack_offset = stack_offset }; |
| 1776 | 1829 | }, |
| 1777 | | 32, 64 => return self.fail("TODO overflow operations on integers u32/i32 and u64/i64", .{}), |
| 1830 | 32, 64 => { |
| 1831 | // Only say yes if the operation is |
| 1832 | // commutative, i.e. we can swap both of the |
| 1833 | // operands |
| 1834 | const lhs_immediate_ok = switch (tag) { |
| 1835 | .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 1836 | .sub_with_overflow => false, |
| 1837 | else => unreachable, |
| 1838 | }; |
| 1839 | const rhs_immediate_ok = switch (tag) { |
| 1840 | .add_with_overflow, |
| 1841 | .sub_with_overflow, |
| 1842 | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 1843 | else => unreachable, |
| 1844 | }; |
| 1845 | |
| 1846 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 1847 | .add_with_overflow => .adds_shifted_register, |
| 1848 | .sub_with_overflow => .subs_shifted_register, |
| 1849 | else => unreachable, |
| 1850 | }; |
| 1851 | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { |
| 1852 | .add_with_overflow => .adds_immediate, |
| 1853 | .sub_with_overflow => .subs_immediate, |
| 1854 | else => unreachable, |
| 1855 | }; |
| 1856 | |
| 1857 | try self.spillCompareFlagsIfOccupied(); |
| 1858 | self.compare_flags_inst = inst; |
| 1859 | |
| 1860 | const dest = blk: { |
| 1861 | if (rhs_immediate_ok) { |
| 1862 | break :blk try self.binOpImmediate(mir_tag_immediate, null, lhs, rhs, lhs_ty, false); |
| 1863 | } else if (lhs_immediate_ok) { |
| 1864 | // swap lhs and rhs |
| 1865 | break :blk try self.binOpImmediate(mir_tag_immediate, null, rhs, lhs, rhs_ty, true); |
| 1866 | } else { |
| 1867 | break :blk try self.binOpRegister(mir_tag_register, null, lhs, rhs, lhs_ty, rhs_ty); |
| 1868 | } |
| 1869 | }; |
| 1870 | |
| 1871 | switch (int_info.signedness) { |
| 1872 | .unsigned => break :result MCValue{ .register_c_flag = dest.register }, |
| 1873 | .signed => break :result MCValue{ .register_v_flag = dest.register }, |
| 1874 | } |
| 1875 | }, |
| 1778 | 1876 | else => return self.fail("TODO overflow operations on integers > u32/i32", .{}), |
| 1779 | 1877 | } |
| 1780 | 1878 | }, |
| ... | ... | @@ -2148,8 +2246,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2148 | 2246 | .undef => unreachable, |
| 2149 | 2247 | .unreach => unreachable, |
| 2150 | 2248 | .dead => unreachable, |
| 2151 | | .compare_flags_unsigned => unreachable, |
| 2152 | | .compare_flags_signed => unreachable, |
| 2249 | .compare_flags_unsigned, |
| 2250 | .compare_flags_signed, |
| 2251 | .register_c_flag, |
| 2252 | .register_v_flag, |
| 2253 | => unreachable, // cannot hold an address |
| 2153 | 2254 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 2154 | 2255 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), |
| 2155 | 2256 | .register => |addr_reg| { |
| ... | ... | @@ -2366,8 +2467,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 2366 | 2467 | .undef => unreachable, |
| 2367 | 2468 | .unreach => unreachable, |
| 2368 | 2469 | .dead => unreachable, |
| 2369 | | .compare_flags_unsigned => unreachable, |
| 2370 | | .compare_flags_signed => unreachable, |
| 2470 | .compare_flags_unsigned, |
| 2471 | .compare_flags_signed, |
| 2472 | .register_c_flag, |
| 2473 | .register_v_flag, |
| 2474 | => unreachable, // cannot hold an address |
| 2371 | 2475 | .immediate => |imm| { |
| 2372 | 2476 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); |
| 2373 | 2477 | }, |
| ... | ... | @@ -2487,6 +2591,40 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2487 | 2591 | .memory => |addr| { |
| 2488 | 2592 | break :result MCValue{ .memory = addr + struct_field_offset }; |
| 2489 | 2593 | }, |
| 2594 | .register_c_flag, |
| 2595 | .register_v_flag, |
| 2596 | => |reg| { |
| 2597 | switch (index) { |
| 2598 | 0 => { |
| 2599 | // get wrapped value: return register |
| 2600 | break :result MCValue{ .register = reg }; |
| 2601 | }, |
| 2602 | 1 => { |
| 2603 | // TODO return special MCValue condition flags |
| 2604 | // get overflow bit: set register to C flag |
| 2605 | // resp. V flag |
| 2606 | const raw_dest_reg = try self.register_manager.allocReg(null); |
| 2607 | const dest_reg = raw_dest_reg.to32(); |
| 2608 | |
| 2609 | // C flag: cset reg, cs |
| 2610 | // V flag: cset reg, vs |
| 2611 | _ = try self.addInst(.{ |
| 2612 | .tag = .cset, |
| 2613 | .data = .{ .r_cond = .{ |
| 2614 | .rd = dest_reg, |
| 2615 | .cond = switch (mcv) { |
| 2616 | .register_c_flag => .cs, |
| 2617 | .register_v_flag => .vs, |
| 2618 | else => unreachable, |
| 2619 | }, |
| 2620 | } }, |
| 2621 | }); |
| 2622 | |
| 2623 | break :result MCValue{ .register = dest_reg }; |
| 2624 | }, |
| 2625 | else => unreachable, |
| 2626 | } |
| 2627 | }, |
| 2490 | 2628 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), |
| 2491 | 2629 | } |
| 2492 | 2630 | }; |
| ... | ... | @@ -2531,7 +2669,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2531 | 2669 | |
| 2532 | 2670 | switch (mcv) { |
| 2533 | 2671 | .register => |reg| { |
| 2534 | | self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst); |
| 2672 | self.register_manager.getRegAssumeFree(reg, inst); |
| 2535 | 2673 | }, |
| 2536 | 2674 | else => {}, |
| 2537 | 2675 | } |
| ... | ... | @@ -2596,15 +2734,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2596 | 2734 | |
| 2597 | 2735 | switch (mc_arg) { |
| 2598 | 2736 | .none => continue, |
| 2599 | | .undef => unreachable, |
| 2600 | | .immediate => unreachable, |
| 2601 | | .unreach => unreachable, |
| 2602 | | .dead => unreachable, |
| 2603 | | .memory => unreachable, |
| 2604 | | .compare_flags_signed => unreachable, |
| 2605 | | .compare_flags_unsigned => unreachable, |
| 2606 | | .got_load => unreachable, |
| 2607 | | .direct_load => unreachable, |
| 2608 | 2737 | .register => |reg| { |
| 2609 | 2738 | try self.register_manager.getReg(reg, null); |
| 2610 | 2739 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| ... | ... | @@ -2615,6 +2744,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2615 | 2744 | .ptr_stack_offset => { |
| 2616 | 2745 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| 2617 | 2746 | }, |
| 2747 | else => unreachable, |
| 2618 | 2748 | } |
| 2619 | 2749 | } |
| 2620 | 2750 | |
| ... | ... | @@ -3518,6 +3648,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3518 | 3648 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 3519 | 3649 | } |
| 3520 | 3650 | }, |
| 3651 | .register_c_flag, |
| 3652 | .register_v_flag, |
| 3653 | => { |
| 3654 | return self.fail("TODO implement genSetStack {}", .{mcv}); |
| 3655 | }, |
| 3521 | 3656 | .got_load, |
| 3522 | 3657 | .direct_load, |
| 3523 | 3658 | .memory, |
| ... | ... | @@ -3635,7 +3770,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3635 | 3770 | .tag = .cset, |
| 3636 | 3771 | .data = .{ .r_cond = .{ |
| 3637 | 3772 | .rd = reg, |
| 3638 | | .cond = condition.negate(), |
| 3773 | .cond = condition, |
| 3639 | 3774 | } }, |
| 3640 | 3775 | }); |
| 3641 | 3776 | }, |
| ... | ... | @@ -3678,6 +3813,9 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3678 | 3813 | .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, |
| 3679 | 3814 | }); |
| 3680 | 3815 | }, |
| 3816 | .register_c_flag, |
| 3817 | .register_v_flag, |
| 3818 | => unreachable, // doesn't fit into a register |
| 3681 | 3819 | .got_load, |
| 3682 | 3820 | .direct_load, |
| 3683 | 3821 | => |sym_index| { |
| ... | ... | @@ -4279,8 +4417,3 @@ fn registerAlias(reg: Register, size_bytes: u64) Register { |
| 4279 | 4417 | unreachable; // TODO handle floating-point registers |
| 4280 | 4418 | } |
| 4281 | 4419 | } |
| 4282 | | |
| 4283 | | /// Resolves any aliased registers to the 64-bit wide ones. |
| 4284 | | fn toCanonicalReg(reg: Register) Register { |
| 4285 | | return reg.to64(); |
| 4286 | | } |