| ... | ... | @@ -85,6 +85,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 85 | 85 | register_manager: RegisterManager = .{}, |
| 86 | 86 | /// Maps offset to what is stored there. |
| 87 | 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 | 91 | /// Offset from the stack base, representing the end of the stack frame. |
| 90 | 92 | max_end_stack: u32 = 0, |
| ... | ... | @@ -722,6 +724,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 722 | 724 | const canon_reg = toCanonicalReg(reg); |
| 723 | 725 | self.register_manager.freeReg(canon_reg); |
| 724 | 726 | }, |
| 727 | .compare_flags_signed, .compare_flags_unsigned => { |
| 728 | self.compare_flags_inst = null; |
| 729 | }, |
| 725 | 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 | 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 | 883 | /// Copies a value to a register without tracking the register. The register is not considered |
| 861 | 884 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 862 | 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 | 2425 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 2403 | 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 | 2438 | for (info.args) |mc_arg, arg_i| { |
| 2406 | 2439 | const arg = args[arg_i]; |
| 2407 | 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 | 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 | 2632 | const signedness: std.builtin.Signedness = blk: { |
| 2597 | 2633 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 2598 | 2634 | if (ty.zigTypeTag() != .Int) break :blk .unsigned; |
| ... | ... | @@ -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 | 2805 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 2759 | 2806 | const parent_next_stack_offset = self.next_stack_offset; |
| 2760 | 2807 | const parent_free_registers = self.register_manager.free_registers; |
| 2761 | 2808 | var parent_stack = try self.stack.clone(self.gpa); |
| 2762 | 2809 | defer parent_stack.deinit(self.gpa); |
| 2763 | 2810 | const parent_registers = self.register_manager.registers; |
| 2811 | const parent_compare_flags_inst = self.compare_flags_inst; |
| 2764 | 2812 | |
| 2765 | 2813 | try self.branch_stack.append(.{}); |
| 2766 | 2814 | |
| ... | ... | @@ -2776,6 +2824,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2776 | 2824 | defer saved_then_branch.deinit(self.gpa); |
| 2777 | 2825 | |
| 2778 | 2826 | self.register_manager.registers = parent_registers; |
| 2827 | self.compare_flags_inst = parent_compare_flags_inst; |
| 2779 | 2828 | |
| 2780 | 2829 | self.stack.deinit(self.gpa); |
| 2781 | 2830 | self.stack = parent_stack; |
| ... | ... | @@ -2867,7 +2916,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2867 | 2916 | |
| 2868 | 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 | 2924 | fn isNull(self: *Self, operand: MCValue) !MCValue { |
| ... | ... | @@ -2885,8 +2936,6 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { |
| 2885 | 2936 | } |
| 2886 | 2937 | |
| 2887 | 2938 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2888 | | _ = operand; |
| 2889 | | |
| 2890 | 2939 | const error_type = ty.errorUnionSet(); |
| 2891 | 2940 | const payload_type = ty.errorUnionPayload(); |
| 2892 | 2941 | |