| ... | @@ -85,6 +85,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, | ... | @@ -85,6 +85,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 85 | register_manager: RegisterManager = .{}, | 85 | register_manager: RegisterManager = .{}, |
| 86 | /// Maps offset to what is stored there. | 86 | /// Maps offset to what is stored there. |
| 87 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, | 87 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| | 88 | /// Tracks the current instruction allocated to the compare flags |
| | 89 | compare_flags_inst: ?Air.Inst.Index = null, |
| 88 | | 90 | |
| 89 | /// Offset from the stack base, representing the end of the stack frame. | 91 | /// Offset from the stack base, representing the end of the stack frame. |
| 90 | max_end_stack: u32 = 0, | 92 | max_end_stack: u32 = 0, |
| ... | @@ -722,6 +724,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { | ... | @@ -722,6 +724,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 722 | const canon_reg = toCanonicalReg(reg); | 724 | const canon_reg = toCanonicalReg(reg); |
| 723 | self.register_manager.freeReg(canon_reg); | 725 | self.register_manager.freeReg(canon_reg); |
| 724 | }, | 726 | }, |
| | 727 | .compare_flags_signed, .compare_flags_unsigned => { |
| | 728 | self.compare_flags_inst = null; |
| | 729 | }, |
| 725 | else => {}, // TODO process stack allocation death | 730 | else => {}, // TODO process stack allocation death |
| 726 | } | 731 | } |
| 727 | } | 732 | } |
| ... | @@ -857,6 +862,24 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void | ... | @@ -857,6 +862,24 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 857 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); | 862 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); |
| 858 | } | 863 | } |
| 859 | | 864 | |
| | 865 | /// Save the current instruction stored in the compare flags if |
| | 866 | /// occupied |
| | 867 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| | 868 | if (self.compare_flags_inst) |inst_to_save| { |
| | 869 | const mcv = self.getResolvedInstValue(inst_to_save); |
| | 870 | assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); |
| | 871 | |
| | 872 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| | 873 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| | 874 | log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); |
| | 875 | |
| | 876 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| | 877 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); |
| | 878 | |
| | 879 | self.compare_flags_inst = null; |
| | 880 | } |
| | 881 | } |
| | 882 | |
| 860 | /// Copies a value to a register without tracking the register. The register is not considered | 883 | /// Copies a value to a register without tracking the register. The register is not considered |
| 861 | /// allocated. A second call to `copyToTmpRegister` may return the same register. | 884 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 862 | /// This can have a side effect of spilling instructions to the stack to free up a register. | 885 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| ... | @@ -2402,6 +2425,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2402,6 +2425,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2402 | var info = try self.resolveCallingConventionValues(fn_ty); | 2425 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 2403 | defer info.deinit(self); | 2426 | defer info.deinit(self); |
| 2404 | | 2427 | |
| | 2428 | // According to the Procedure Call Standard for the ARM |
| | 2429 | // Architecture, compare flags are not preserved across |
| | 2430 | // calls. Therefore, if some value is currently stored there, we |
| | 2431 | // need to save it. |
| | 2432 | // |
| | 2433 | // TODO once caller-saved registers are implemented, save them |
| | 2434 | // here too, but crucially *after* we save the compare flags as |
| | 2435 | // saving compare flags may require a new caller-saved register |
| | 2436 | try self.spillCompareFlagsIfOccupied(); |
| | 2437 | |
| 2405 | for (info.args) |mc_arg, arg_i| { | 2438 | for (info.args) |mc_arg, arg_i| { |
| 2406 | const arg = args[arg_i]; | 2439 | const arg = args[arg_i]; |
| 2407 | const arg_ty = self.air.typeOf(arg); | 2440 | const arg_ty = self.air.typeOf(arg); |
| ... | @@ -2593,6 +2626,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -2593,6 +2626,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2593 | return self.fail("TODO cmp for types with size > 8", .{}); | 2626 | return self.fail("TODO cmp for types with size > 8", .{}); |
| 2594 | } | 2627 | } |
| 2595 | | 2628 | |
| | 2629 | try self.spillCompareFlagsIfOccupied(); |
| | 2630 | self.compare_flags_inst = inst; |
| | 2631 | |
| 2596 | const signedness: std.builtin.Signedness = blk: { | 2632 | const signedness: std.builtin.Signedness = blk: { |
| 2597 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | 2633 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 2598 | if (ty.zigTypeTag() != .Int) break :blk .unsigned; | 2634 | if (ty.zigTypeTag() != .Int) break :blk .unsigned; |
| ... | @@ -2755,12 +2791,24 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2755,12 +2791,24 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2755 | }, | 2791 | }, |
| 2756 | }; | 2792 | }; |
| 2757 | | 2793 | |
| | 2794 | // If the condition dies here in this condbr instruction, process |
| | 2795 | // that death now instead of later as this has an effect on |
| | 2796 | // whether it needs to be spilled in the branches |
| | 2797 | if (self.liveness.operandDies(inst, 0)) { |
| | 2798 | const op_int = @enumToInt(pl_op.operand); |
| | 2799 | if (op_int >= Air.Inst.Ref.typed_value_map.len) { |
| | 2800 | const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); |
| | 2801 | self.processDeath(op_index); |
| | 2802 | } |
| | 2803 | } |
| | 2804 | |
| 2758 | // Capture the state of register and stack allocation state so that we can revert to it. | 2805 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 2759 | const parent_next_stack_offset = self.next_stack_offset; | 2806 | const parent_next_stack_offset = self.next_stack_offset; |
| 2760 | const parent_free_registers = self.register_manager.free_registers; | 2807 | const parent_free_registers = self.register_manager.free_registers; |
| 2761 | var parent_stack = try self.stack.clone(self.gpa); | 2808 | var parent_stack = try self.stack.clone(self.gpa); |
| 2762 | defer parent_stack.deinit(self.gpa); | 2809 | defer parent_stack.deinit(self.gpa); |
| 2763 | const parent_registers = self.register_manager.registers; | 2810 | const parent_registers = self.register_manager.registers; |
| | 2811 | const parent_compare_flags_inst = self.compare_flags_inst; |
| 2764 | | 2812 | |
| 2765 | try self.branch_stack.append(.{}); | 2813 | try self.branch_stack.append(.{}); |
| 2766 | | 2814 | |
| ... | @@ -2776,6 +2824,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2776,6 +2824,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2776 | defer saved_then_branch.deinit(self.gpa); | 2824 | defer saved_then_branch.deinit(self.gpa); |
| 2777 | | 2825 | |
| 2778 | self.register_manager.registers = parent_registers; | 2826 | self.register_manager.registers = parent_registers; |
| | 2827 | self.compare_flags_inst = parent_compare_flags_inst; |
| 2779 | | 2828 | |
| 2780 | self.stack.deinit(self.gpa); | 2829 | self.stack.deinit(self.gpa); |
| 2781 | self.stack = parent_stack; | 2830 | self.stack = parent_stack; |
| ... | @@ -2867,7 +2916,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2867,7 +2916,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2867 | | 2916 | |
| 2868 | self.branch_stack.pop().deinit(self.gpa); | 2917 | self.branch_stack.pop().deinit(self.gpa); |
| 2869 | | 2918 | |
| 2870 | return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); | 2919 | // We already took care of pl_op.operand earlier, so we're going |
| | 2920 | // to pass .none here |
| | 2921 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 2871 | } | 2922 | } |
| 2872 | | 2923 | |
| 2873 | fn isNull(self: *Self, operand: MCValue) !MCValue { | 2924 | fn isNull(self: *Self, operand: MCValue) !MCValue { |
| ... | @@ -2885,8 +2936,6 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { | ... | @@ -2885,8 +2936,6 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { |
| 2885 | } | 2936 | } |
| 2886 | | 2937 | |
| 2887 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | 2938 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2888 | _ = operand; | | |
| 2889 | | | |
| 2890 | const error_type = ty.errorUnionSet(); | 2939 | const error_type = ty.errorUnionSet(); |
| 2891 | const payload_type = ty.errorUnionPayload(); | 2940 | const payload_type = ty.errorUnionPayload(); |
| 2892 | | 2941 | |