authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-12-18 14:41:01+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-18 15:23:25-08:00
log9892684d3555193f6b9d9e6d52761cd5ea829b85
treeb6ca77a9908fc32f149b29040db6bfbb3201cc79
parent5b29b4ffa6d21d574ed6cb8a00e2aba9dcb79ff8

stage2 ARM: spill insts currently in compare flags if necessary


2 files changed, 80 insertions(+), 4 deletions(-)

src/arch/arm/CodeGen.zig+59-4
...@@ -76,6 +76,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},...@@ -76,6 +76,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
76register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},76register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
77/// Maps offset to what is stored there.77/// Maps offset to what is stored there.
78stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},78stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
79/// Tracks the current instruction allocated to the compare flags
80compare_flags_inst: ?Air.Inst.Index = null,
7981
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.
81max_end_stack: u32 = 0,83max_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 death655 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}
781786
787/// Save the current instruction stored in the compare flags if
788/// occupied
789fn 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 considered809/// 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);
18331860
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 stack1871 // 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;
18361873
...@@ -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", .{});
19862023
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 };
20962136
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;
21032155
2104 try self.branch_stack.append(.{});2156 try self.branch_stack.append(.{});
21052157
...@@ -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);
21162168
2117 self.register_manager.registers = parent_registers;2169 self.register_manager.registers = parent_registers;
2170 self.compare_flags_inst = parent_compare_flags_inst;
21182171
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 {
22062259
2207 self.branch_stack.pop().deinit(self.gpa);2260 self.branch_stack.pop().deinit(self.gpa);
22082261
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}
22112266
2212fn isNull(self: *Self, operand: MCValue) !MCValue {2267fn isNull(self: *Self, operand: MCValue) !MCValue {
test/stage2/arm.zig+21
...@@ -547,4 +547,25 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -547,4 +547,25 @@ pub fn addCases(ctx: *TestContext) !void {
547 ,547 ,
548 );548 );
549 }549 }
550
551 {
552 var case = ctx.exe("save compare flags", linux_arm);
553 case.addCompareOutput(
554 \\pub fn main() void {
555 \\ foo(2, 1);
556 \\}
557 \\
558 \\fn foo(x: u32, y: u32) void {
559 \\ const b = x > y;
560 \\ assert(b);
561 \\ assert(b);
562 \\}
563 \\
564 \\pub fn assert(ok: bool) void {
565 \\ if (!ok) unreachable; // assertion failure
566 \\}
567 ,
568 "",
569 );
570 }
550}571}