authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-03-11 14:12:11+01:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-03-11 14:12:11+01:00
log4590e980f77f406158258c8f44a76e28d53eff2f
treeba874ceb64ee6c2de89223dbf87d4b0e0018d78f
parent06058ed6f38194dfe74d232e77e33d98919d1ee7
signaturelock-open Commit is signed but in an unrecognized format.

stage2 ARM: implement caller-saved registers


3 files changed, 40 insertions(+), 32 deletions(-)

src/arch/arm/CodeGen.zig+21-18
...@@ -22,7 +22,7 @@ const leb128 = std.leb;...@@ -22,7 +22,7 @@ const leb128 = std.leb;
22const log = std.log.scoped(.codegen);22const log = std.log.scoped(.codegen);
23const build_options = @import("build_options");23const build_options = @import("build_options");
24const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;24const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
25const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);25const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers);
2626
27const FnResult = @import("../../codegen.zig").FnResult;27const FnResult = @import("../../codegen.zig").FnResult;
28const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;28const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
...@@ -34,6 +34,8 @@ const Register = bits.Register;...@@ -34,6 +34,8 @@ const Register = bits.Register;
34const Instruction = bits.Instruction;34const Instruction = bits.Instruction;
35const Condition = bits.Condition;35const Condition = bits.Condition;
36const callee_preserved_regs = abi.callee_preserved_regs;36const callee_preserved_regs = abi.callee_preserved_regs;
37const caller_preserved_regs = abi.caller_preserved_regs;
38const allocatable_registers = abi.allocatable_registers;
37const c_abi_int_param_regs = abi.c_abi_int_param_regs;39const c_abi_int_param_regs = abi.c_abi_int_param_regs;
38const c_abi_int_return_regs = abi.c_abi_int_return_regs;40const c_abi_int_return_regs = abi.c_abi_int_return_regs;
3941
...@@ -788,10 +790,6 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {...@@ -788,10 +790,6 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
788 if (!elem_ty.hasRuntimeBits()) {790 if (!elem_ty.hasRuntimeBits()) {
789 // As this stack item will never be dereferenced at runtime,791 // As this stack item will never be dereferenced at runtime,
790 // return the current stack offset792 // return the current stack offset
791 try self.stack.putNoClobber(self.gpa, self.next_stack_offset, .{
792 .inst = inst,
793 .size = 0,
794 });
795 return self.next_stack_offset;793 return self.next_stack_offset;
796 }794 }
797795
...@@ -1569,13 +1567,13 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind...@@ -1569,13 +1567,13 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
15691567
1570 switch (mcv) {1568 switch (mcv) {
1571 .register => |reg| {1569 .register => |reg| {
1572 // If it's in the registers table, need to associate the register with the1570 // We assert that this register is allocatable by asking
1573 // new instruction.1571 // for its index
1574 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {1572 const index = RegisterManager.indexOfRegIntoTracked(reg).?; // see note above
1575 if (!self.register_manager.isRegFree(reg)) {1573 if (!self.register_manager.isRegFree(reg)) {
1576 self.register_manager.registers[index] = inst;1574 self.register_manager.registers[index] = inst;
1577 }
1578 }1575 }
1576
1579 log.debug("%{d} => {} (reused)", .{ inst, reg });1577 log.debug("%{d} => {} (reused)", .{ inst, reg });
1580 },1578 },
1581 .stack_offset => |off| {1579 .stack_offset => |off| {
...@@ -2545,13 +2543,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -2545,13 +2543,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
2545 // Architecture, compare flags are not preserved across2543 // Architecture, compare flags are not preserved across
2546 // calls. Therefore, if some value is currently stored there, we2544 // calls. Therefore, if some value is currently stored there, we
2547 // need to save it.2545 // need to save it.
2548 //
2549 // TODO once caller-saved registers are implemented, save them
2550 // here too, but crucially *after* we save the compare flags as
2551 // saving compare flags may require a new caller-saved register
2552 try self.spillCompareFlagsIfOccupied();2546 try self.spillCompareFlagsIfOccupied();
25532547
2548 // Save caller-saved registers, but crucially *after* we save the
2549 // compare flags as saving compare flags may require a new
2550 // caller-saved register
2551 for (caller_preserved_regs) |reg| {
2552 try self.register_manager.getReg(reg, null);
2553 }
2554
2554 if (info.return_value == .stack_offset) {2555 if (info.return_value == .stack_offset) {
2556 log.debug("airCall: return by reference", .{});
2555 const ret_ty = fn_ty.fnReturnType();2557 const ret_ty = fn_ty.fnReturnType();
2556 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));2558 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
2557 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));2559 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
...@@ -2562,7 +2564,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -2562,7 +2564,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
2562 .data = ret_ty,2564 .data = ret_ty,
2563 };2565 };
2564 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);2566 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2565 try self.register_manager.getReg(.r0, inst);2567 try self.register_manager.getReg(.r0, null);
2566 try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset });2568 try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset });
25672569
2568 info.return_value = .{ .stack_offset = stack_offset };2570 info.return_value = .{ .stack_offset = stack_offset };
...@@ -2662,8 +2664,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -2662,8 +2664,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
2662 const result: MCValue = result: {2664 const result: MCValue = result: {
2663 switch (info.return_value) {2665 switch (info.return_value) {
2664 .register => |reg| {2666 .register => |reg| {
2665 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {2667 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {
2666 // Save function return value in a callee saved register2668 // Save function return value into a tracked register
2669 log.debug("airCall: copying {} as it is not tracked", .{reg});
2667 break :result try self.copyToNewRegister(inst, info.return_value);2670 break :result try self.copyToNewRegister(inst, info.return_value);
2668 }2671 }
2669 },2672 },
src/arch/arm/abi.zig+3
...@@ -2,5 +2,8 @@ const bits = @import("bits.zig");...@@ -2,5 +2,8 @@ const bits = @import("bits.zig");
2const Register = bits.Register;2const Register = bits.Register;
33
4pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };4pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
5pub const caller_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3 };
6pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs;
7
5pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };8pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
6pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };9pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
src/register_manager.zig+16-14
...@@ -26,11 +26,11 @@ pub const AllocateRegistersError = error{...@@ -26,11 +26,11 @@ pub const AllocateRegistersError = error{
26pub fn RegisterManager(26pub fn RegisterManager(
27 comptime Function: type,27 comptime Function: type,
28 comptime Register: type,28 comptime Register: type,
29 comptime callee_preserved_regs: []const Register,29 comptime tracked_registers: []const Register,
30) type {30) type {
31 // architectures which do not have a concept of registers should31 // architectures which do not have a concept of registers should
32 // refrain from using RegisterManager32 // refrain from using RegisterManager
33 assert(callee_preserved_regs.len > 0); // see note above33 assert(tracked_registers.len > 0); // see note above
3434
35 return struct {35 return struct {
36 /// Tracks the AIR instruction allocated to every register. If36 /// Tracks the AIR instruction allocated to every register. If
...@@ -38,7 +38,7 @@ pub fn RegisterManager(...@@ -38,7 +38,7 @@ pub fn RegisterManager(
38 /// register is free), the value in that slot is undefined.38 /// register is free), the value in that slot is undefined.
39 ///39 ///
40 /// The key must be canonical register.40 /// The key must be canonical register.
41 registers: [callee_preserved_regs.len]Air.Inst.Index = undefined,41 registers: [tracked_registers.len]Air.Inst.Index = undefined,
42 /// Tracks which registers are free (in which case the42 /// Tracks which registers are free (in which case the
43 /// corresponding bit is set to 1)43 /// corresponding bit is set to 1)
44 free_registers: FreeRegInt = math.maxInt(FreeRegInt),44 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
...@@ -53,7 +53,7 @@ pub fn RegisterManager(...@@ -53,7 +53,7 @@ pub fn RegisterManager(
5353
54 /// An integer whose bits represent all the registers and54 /// An integer whose bits represent all the registers and
55 /// whether they are free.55 /// whether they are free.
56 const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len);56 const FreeRegInt = std.meta.Int(.unsigned, tracked_registers.len);
57 const ShiftInt = math.Log2Int(FreeRegInt);57 const ShiftInt = math.Log2Int(FreeRegInt);
5858
59 fn getFunction(self: *Self) *Function {59 fn getFunction(self: *Self) *Function {
...@@ -83,14 +83,14 @@ pub fn RegisterManager(...@@ -83,14 +83,14 @@ pub fn RegisterManager(
83 }83 }
8484
85 pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) {85 pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) {
86 inline for (callee_preserved_regs) |cpreg, i| {86 inline for (tracked_registers) |cpreg, i| {
87 if (reg.id() == cpreg.id()) return i;87 if (reg.id() == cpreg.id()) return i;
88 }88 }
89 return null;89 return null;
90 }90 }
9191
92 pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt {92 pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt {
93 return indexOfReg(callee_preserved_regs, reg);93 return indexOfReg(tracked_registers, reg);
94 }94 }
9595
96 /// Returns true when this register is not tracked96 /// Returns true when this register is not tracked
...@@ -146,14 +146,14 @@ pub fn RegisterManager(...@@ -146,14 +146,14 @@ pub fn RegisterManager(
146 comptime count: comptime_int,146 comptime count: comptime_int,
147 insts: [count]?Air.Inst.Index,147 insts: [count]?Air.Inst.Index,
148 ) ?[count]Register {148 ) ?[count]Register {
149 comptime assert(count > 0 and count <= callee_preserved_regs.len);149 comptime assert(count > 0 and count <= tracked_registers.len);
150150
151 const free_registers = @popCount(FreeRegInt, self.free_registers);151 const free_registers = @popCount(FreeRegInt, self.free_registers);
152 if (free_registers < count) return null;152 if (free_registers < count) return null;
153153
154 var regs: [count]Register = undefined;154 var regs: [count]Register = undefined;
155 var i: usize = 0;155 var i: usize = 0;
156 for (callee_preserved_regs) |reg| {156 for (tracked_registers) |reg| {
157 if (i >= count) break;157 if (i >= count) break;
158 if (self.isRegFrozen(reg)) continue;158 if (self.isRegFrozen(reg)) continue;
159 if (self.isRegFree(reg)) {159 if (self.isRegFree(reg)) {
...@@ -192,8 +192,8 @@ pub fn RegisterManager(...@@ -192,8 +192,8 @@ pub fn RegisterManager(
192 comptime count: comptime_int,192 comptime count: comptime_int,
193 insts: [count]?Air.Inst.Index,193 insts: [count]?Air.Inst.Index,
194 ) AllocateRegistersError![count]Register {194 ) AllocateRegistersError![count]Register {
195 comptime assert(count > 0 and count <= callee_preserved_regs.len);195 comptime assert(count > 0 and count <= tracked_registers.len);
196 if (count > callee_preserved_regs.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters;196 if (count > tracked_registers.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters;
197197
198 const result = self.tryAllocRegs(count, insts) orelse blk: {198 const result = self.tryAllocRegs(count, insts) orelse blk: {
199 // We'll take over the first count registers. Spill199 // We'll take over the first count registers. Spill
...@@ -201,7 +201,7 @@ pub fn RegisterManager(...@@ -201,7 +201,7 @@ pub fn RegisterManager(
201 // stack allocations.201 // stack allocations.
202 var regs: [count]Register = undefined;202 var regs: [count]Register = undefined;
203 var i: usize = 0;203 var i: usize = 0;
204 for (callee_preserved_regs) |reg| {204 for (tracked_registers) |reg| {
205 if (i >= count) break;205 if (i >= count) break;
206 if (self.isRegFrozen(reg)) continue;206 if (self.isRegFrozen(reg)) continue;
207207
...@@ -247,6 +247,7 @@ pub fn RegisterManager(...@@ -247,6 +247,7 @@ pub fn RegisterManager(
247 /// register.247 /// register.
248 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void {248 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void {
249 const index = indexOfRegIntoTracked(reg) orelse return;249 const index = indexOfRegIntoTracked(reg) orelse return;
250 log.debug("getReg {} for inst {}", .{ reg, inst });
250 self.markRegAllocated(reg);251 self.markRegAllocated(reg);
251252
252 if (inst) |tracked_inst|253 if (inst) |tracked_inst|
...@@ -275,6 +276,7 @@ pub fn RegisterManager(...@@ -275,6 +276,7 @@ pub fn RegisterManager(
275 /// spilling is necessary.276 /// spilling is necessary.
276 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void {277 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void {
277 const index = indexOfRegIntoTracked(reg) orelse return;278 const index = indexOfRegIntoTracked(reg) orelse return;
279 log.debug("getRegAssumeFree {} for inst {}", .{ reg, inst });
278 self.markRegAllocated(reg);280 self.markRegAllocated(reg);
279281
280 assert(self.isRegFree(reg));282 assert(self.isRegFree(reg));
...@@ -303,7 +305,7 @@ const MockRegister1 = enum(u2) {...@@ -303,7 +305,7 @@ const MockRegister1 = enum(u2) {
303 return @enumToInt(reg);305 return @enumToInt(reg);
304 }306 }
305307
306 const callee_preserved_regs = [_]MockRegister1{ .r2, .r3 };308 const allocatable_registers = [_]MockRegister1{ .r2, .r3 };
307};309};
308310
309const MockRegister2 = enum(u2) {311const MockRegister2 = enum(u2) {
...@@ -316,13 +318,13 @@ const MockRegister2 = enum(u2) {...@@ -316,13 +318,13 @@ const MockRegister2 = enum(u2) {
316 return @enumToInt(reg);318 return @enumToInt(reg);
317 }319 }
318320
319 const callee_preserved_regs = [_]MockRegister2{ .r0, .r1, .r2, .r3 };321 const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 };
320};322};
321323
322fn MockFunction(comptime Register: type) type {324fn MockFunction(comptime Register: type) type {
323 return struct {325 return struct {
324 allocator: Allocator,326 allocator: Allocator,
325 register_manager: RegisterManager(Self, Register, &Register.callee_preserved_regs) = .{},327 register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{},
326 spilled: std.ArrayListUnmanaged(Register) = .{},328 spilled: std.ArrayListUnmanaged(Register) = .{},
327329
328 const Self = @This();330 const Self = @This();