| ... | ... | @@ -76,6 +76,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 76 | 76 | register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{}, |
| 77 | 77 | /// Maps offset to what is stored there. |
| 78 | 78 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 79 | /// Tracks the current instruction allocated to the compare flags |
| 80 | compare_flags_inst: ?Air.Inst.Index = null, |
| 79 | 81 | |
| 80 | 82 | /// Offset from the stack base, representing the end of the stack frame. |
| 81 | 83 | max_end_stack: u32 = 0, |
| ... | ... | @@ -647,6 +649,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 647 | 649 | .register => |reg| { |
| 648 | 650 | self.register_manager.freeReg(reg); |
| 649 | 651 | }, |
| 652 | .compare_flags_signed, .compare_flags_unsigned => { |
| 653 | self.compare_flags_inst = null; |
| 654 | }, |
| 650 | 655 | else => {}, // TODO process stack allocation death |
| 651 | 656 | } |
| 652 | 657 | } |
| ... | ... | @@ -779,6 +784,28 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 779 | 784 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); |
| 780 | 785 | } |
| 781 | 786 | |
| 787 | /// Save the current instruction stored in the compare flags if |
| 788 | /// occupied |
| 789 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 790 | if (self.compare_flags_inst) |inst_to_save| { |
| 791 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 792 | assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); |
| 793 | |
| 794 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| 795 | switch (new_mcv) { |
| 796 | .register => |reg| try self.genSetReg(self.air.typeOfIndex(inst_to_save), reg, mcv), |
| 797 | .stack_offset => |offset| try self.genSetStack(self.air.typeOfIndex(inst_to_save), offset, mcv), |
| 798 | else => unreachable, |
| 799 | } |
| 800 | log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); |
| 801 | |
| 802 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 803 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); |
| 804 | |
| 805 | self.compare_flags_inst = null; |
| 806 | } |
| 807 | } |
| 808 | |
| 782 | 809 | /// Copies a value to a register without tracking the register. The register is not considered |
| 783 | 810 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 784 | 811 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| ... | ... | @@ -890,10 +917,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 890 | 917 | }; |
| 891 | 918 | break :result r; |
| 892 | 919 | }, |
| 893 | | else => {}, |
| 920 | else => { |
| 921 | break :result try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not); |
| 922 | }, |
| 894 | 923 | } |
| 895 | | |
| 896 | | break :result try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not); |
| 897 | 924 | }; |
| 898 | 925 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 899 | 926 | } |
| ... | ... | @@ -1831,6 +1858,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1831 | 1858 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 1832 | 1859 | defer info.deinit(self); |
| 1833 | 1860 | |
| 1861 | // According to the Procedure Call Standard for the ARM |
| 1862 | // Architecture, compare flags are not preserved across |
| 1863 | // calls. Therefore, if some value is currently stored there, we |
| 1864 | // need to save it. |
| 1865 | // |
| 1866 | // TODO once caller-saved registers are implemented, save them |
| 1867 | // here too, but crucially *after* we save the compare flags as |
| 1868 | // saving compare flags may require a new caller-saved register |
| 1869 | try self.spillCompareFlagsIfOccupied(); |
| 1870 | |
| 1834 | 1871 | // Make space for the arguments passed via the stack |
| 1835 | 1872 | self.max_end_stack += info.stack_byte_count; |
| 1836 | 1873 | |
| ... | ... | @@ -1984,6 +2021,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1984 | 2021 | if (ty.zigTypeTag() == .ErrorSet) |
| 1985 | 2022 | return self.fail("TODO implement cmp for errors", .{}); |
| 1986 | 2023 | |
| 2024 | try self.spillCompareFlagsIfOccupied(); |
| 2025 | self.compare_flags_inst = inst; |
| 2026 | |
| 1987 | 2027 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1988 | 2028 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1989 | 2029 | const result: MCValue = result: { |
| ... | ... | @@ -2094,12 +2134,24 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2094 | 2134 | }); |
| 2095 | 2135 | }; |
| 2096 | 2136 | |
| 2137 | // If the condition dies here in this condbr instruction, process |
| 2138 | // that death now instead of later as this has an effect on |
| 2139 | // whether it needs to be spilled in the branches |
| 2140 | if (self.liveness.operandDies(inst, 0)) { |
| 2141 | const op_int = @enumToInt(pl_op.operand); |
| 2142 | if (op_int >= Air.Inst.Ref.typed_value_map.len) { |
| 2143 | const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); |
| 2144 | self.processDeath(op_index); |
| 2145 | } |
| 2146 | } |
| 2147 | |
| 2097 | 2148 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 2098 | 2149 | const parent_next_stack_offset = self.next_stack_offset; |
| 2099 | 2150 | const parent_free_registers = self.register_manager.free_registers; |
| 2100 | 2151 | var parent_stack = try self.stack.clone(self.gpa); |
| 2101 | 2152 | defer parent_stack.deinit(self.gpa); |
| 2102 | 2153 | const parent_registers = self.register_manager.registers; |
| 2154 | const parent_compare_flags_inst = self.compare_flags_inst; |
| 2103 | 2155 | |
| 2104 | 2156 | try self.branch_stack.append(.{}); |
| 2105 | 2157 | |
| ... | ... | @@ -2115,6 +2167,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2115 | 2167 | defer saved_then_branch.deinit(self.gpa); |
| 2116 | 2168 | |
| 2117 | 2169 | self.register_manager.registers = parent_registers; |
| 2170 | self.compare_flags_inst = parent_compare_flags_inst; |
| 2118 | 2171 | |
| 2119 | 2172 | self.stack.deinit(self.gpa); |
| 2120 | 2173 | self.stack = parent_stack; |
| ... | ... | @@ -2206,7 +2259,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2206 | 2259 | |
| 2207 | 2260 | self.branch_stack.pop().deinit(self.gpa); |
| 2208 | 2261 | |
| 2209 | | return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); |
| 2262 | // We already took care of pl_op.operand earlier, so we're going |
| 2263 | // to pass .none here |
| 2264 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 2210 | 2265 | } |
| 2211 | 2266 | |
| 2212 | 2267 | fn isNull(self: *Self, operand: MCValue) !MCValue { |