authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-03-12 19:44:41+01:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-03-13 11:32:08+01:00
log384e4ddb061634ae6c62e8c22c8876a818c1e2a3
tree71d5c331b90d0d8227eeaa82fa26b2640ae88238
parent12207bbbd620903c81b0134816e71e373751b2d6
signaturelock-open Commit is signed but in an unrecognized format.

stage2 AArch64: spill compare flags when necessary


1 files changed, 52 insertions(+), 3 deletions(-)

src/arch/aarch64/CodeGen.zig+52-3
...@@ -85,6 +85,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},...@@ -85,6 +85,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
85register_manager: RegisterManager = .{},85register_manager: RegisterManager = .{},
86/// Maps offset to what is stored there.86/// Maps offset to what is stored there.
87stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},87stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
88/// Tracks the current instruction allocated to the compare flags
89compare_flags_inst: ?Air.Inst.Index = null,
8890
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.
90max_end_stack: u32 = 0,92max_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 death730 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}
859864
865/// Save the current instruction stored in the compare flags if
866/// occupied
867fn 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 considered883/// 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);
24042427
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 }
25952628
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 };
27572793
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;
27642812
2765 try self.branch_stack.append(.{});2813 try self.branch_stack.append(.{});
27662814
...@@ -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);
27772825
2778 self.register_manager.registers = parent_registers;2826 self.register_manager.registers = parent_registers;
2827 self.compare_flags_inst = parent_compare_flags_inst;
27792828
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 {
28672916
2868 self.branch_stack.pop().deinit(self.gpa);2917 self.branch_stack.pop().deinit(self.gpa);
28692918
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}
28722923
2873fn isNull(self: *Self, operand: MCValue) !MCValue {2924fn 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}
28862937
2887fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {2938fn 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();
28922941