| ... | @@ -76,6 +76,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, | ... | @@ -76,6 +76,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 76 | register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{}, | 76 | register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{}, |
| 77 | /// Maps offset to what is stored there. | 77 | /// Maps offset to what is stored there. |
| 78 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, | 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 | /// Offset from the stack base, representing the end of the stack frame. | 82 | /// Offset from the stack base, representing the end of the stack frame. |
| 81 | max_end_stack: u32 = 0, | 83 | max_end_stack: u32 = 0, |
| ... | @@ -647,6 +649,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { | ... | @@ -647,6 +649,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 647 | .register => |reg| { | 649 | .register => |reg| { |
| 648 | self.register_manager.freeReg(reg); | 650 | self.register_manager.freeReg(reg); |
| 649 | }, | 651 | }, |
| | 652 | .compare_flags_signed, .compare_flags_unsigned => { |
| | 653 | self.compare_flags_inst = null; |
| | 654 | }, |
| 650 | else => {}, // TODO process stack allocation death | 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,6 +784,28 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 779 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); | 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 | /// Copies a value to a register without tracking the register. The register is not considered | 809 | /// Copies a value to a register without tracking the register. The register is not considered |
| 783 | /// allocated. A second call to `copyToTmpRegister` may return the same register. | 810 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 784 | /// This can have a side effect of spilling instructions to the stack to free up a register. | 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,10 +917,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 890 | }; | 917 | }; |
| 891 | break :result r; | 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 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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,6 +1858,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1831 | var info = try self.resolveCallingConventionValues(fn_ty); | 1858 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 1832 | defer info.deinit(self); | 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 | // Make space for the arguments passed via the stack | 1871 | // Make space for the arguments passed via the stack |
| 1835 | self.max_end_stack += info.stack_byte_count; | 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,6 +2021,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1984 | if (ty.zigTypeTag() == .ErrorSet) | 2021 | if (ty.zigTypeTag() == .ErrorSet) |
| 1985 | return self.fail("TODO implement cmp for errors", .{}); | 2022 | return self.fail("TODO implement cmp for errors", .{}); |
| 1986 | | 2023 | |
| | 2024 | try self.spillCompareFlagsIfOccupied(); |
| | 2025 | self.compare_flags_inst = inst; |
| | 2026 | |
| 1987 | const lhs = try self.resolveInst(bin_op.lhs); | 2027 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1988 | const rhs = try self.resolveInst(bin_op.rhs); | 2028 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1989 | const result: MCValue = result: { | 2029 | const result: MCValue = result: { |
| ... | @@ -2094,12 +2134,24 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -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 | // Capture the state of register and stack allocation state so that we can revert to it. | 2148 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 2098 | const parent_next_stack_offset = self.next_stack_offset; | 2149 | const parent_next_stack_offset = self.next_stack_offset; |
| 2099 | const parent_free_registers = self.register_manager.free_registers; | 2150 | const parent_free_registers = self.register_manager.free_registers; |
| 2100 | var parent_stack = try self.stack.clone(self.gpa); | 2151 | var parent_stack = try self.stack.clone(self.gpa); |
| 2101 | defer parent_stack.deinit(self.gpa); | 2152 | defer parent_stack.deinit(self.gpa); |
| 2102 | const parent_registers = self.register_manager.registers; | 2153 | const parent_registers = self.register_manager.registers; |
| | 2154 | const parent_compare_flags_inst = self.compare_flags_inst; |
| 2103 | | 2155 | |
| 2104 | try self.branch_stack.append(.{}); | 2156 | try self.branch_stack.append(.{}); |
| 2105 | | 2157 | |
| ... | @@ -2115,6 +2167,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2115,6 +2167,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2115 | defer saved_then_branch.deinit(self.gpa); | 2167 | defer saved_then_branch.deinit(self.gpa); |
| 2116 | | 2168 | |
| 2117 | self.register_manager.registers = parent_registers; | 2169 | self.register_manager.registers = parent_registers; |
| | 2170 | self.compare_flags_inst = parent_compare_flags_inst; |
| 2118 | | 2171 | |
| 2119 | self.stack.deinit(self.gpa); | 2172 | self.stack.deinit(self.gpa); |
| 2120 | self.stack = parent_stack; | 2173 | self.stack = parent_stack; |
| ... | @@ -2206,7 +2259,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2206,7 +2259,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2206 | | 2259 | |
| 2207 | self.branch_stack.pop().deinit(self.gpa); | 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 | fn isNull(self: *Self, operand: MCValue) !MCValue { | 2267 | fn isNull(self: *Self, operand: MCValue) !MCValue { |