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;
2222const log = std.log.scoped(.codegen);
2323const build_options = @import("build_options");
2424const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
25const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);
25const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers);
2626
2727const FnResult = @import("../../codegen.zig").FnResult;
2828const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
......@@ -34,6 +34,8 @@ const Register = bits.Register;
3434const Instruction = bits.Instruction;
3535const Condition = bits.Condition;
3636const callee_preserved_regs = abi.callee_preserved_regs;
37const caller_preserved_regs = abi.caller_preserved_regs;
38const allocatable_registers = abi.allocatable_registers;
3739const c_abi_int_param_regs = abi.c_abi_int_param_regs;
3840const 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 {
788790 if (!elem_ty.hasRuntimeBits()) {
789791 // As this stack item will never be dereferenced at runtime,
790792 // return the current stack offset
791 try self.stack.putNoClobber(self.gpa, self.next_stack_offset, .{
792 .inst = inst,
793 .size = 0,
794 });
795793 return self.next_stack_offset;
796794 }
797795
......@@ -1569,13 +1567,13 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
15691567
15701568 switch (mcv) {
15711569 .register => |reg| {
1572 // If it's in the registers table, need to associate the register with the
1573 // new instruction.
1574 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| {
1575 if (!self.register_manager.isRegFree(reg)) {
1576 self.register_manager.registers[index] = inst;
1577 }
1570 // We assert that this register is allocatable by asking
1571 // for its index
1572 const index = RegisterManager.indexOfRegIntoTracked(reg).?; // see note above
1573 if (!self.register_manager.isRegFree(reg)) {
1574 self.register_manager.registers[index] = inst;
15781575 }
1576
15791577 log.debug("%{d} => {} (reused)", .{ inst, reg });
15801578 },
15811579 .stack_offset => |off| {
......@@ -2545,13 +2543,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
25452543 // Architecture, compare flags are not preserved across
25462544 // calls. Therefore, if some value is currently stored there, we
25472545 // 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
25522546 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
25542555 if (info.return_value == .stack_offset) {
2556 log.debug("airCall: return by reference", .{});
25552557 const ret_ty = fn_ty.fnReturnType();
25562558 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
25572559 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.
25622564 .data = ret_ty,
25632565 };
25642566 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);
25662568 try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset });
25672569
25682570 info.return_value = .{ .stack_offset = stack_offset };
......@@ -2662,8 +2664,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
26622664 const result: MCValue = result: {
26632665 switch (info.return_value) {
26642666 .register => |reg| {
2665 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) {
2666 // Save function return value in a callee saved register
2667 if (RegisterManager.indexOfRegIntoTracked(reg) == null) {
2668 // Save function return value into a tracked register
2669 log.debug("airCall: copying {} as it is not tracked", .{reg});
26672670 break :result try self.copyToNewRegister(inst, info.return_value);
26682671 }
26692672 },
src/arch/arm/abi.zig+3
......@@ -2,5 +2,8 @@ const bits = @import("bits.zig");
22const Register = bits.Register;
33
44pub 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
58pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
69pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
src/register_manager.zig+16-14
......@@ -26,11 +26,11 @@ pub const AllocateRegistersError = error{
2626pub fn RegisterManager(
2727 comptime Function: type,
2828 comptime Register: type,
29 comptime callee_preserved_regs: []const Register,
29 comptime tracked_registers: []const Register,
3030) type {
3131 // architectures which do not have a concept of registers should
3232 // refrain from using RegisterManager
33 assert(callee_preserved_regs.len > 0); // see note above
33 assert(tracked_registers.len > 0); // see note above
3434
3535 return struct {
3636 /// Tracks the AIR instruction allocated to every register. If
......@@ -38,7 +38,7 @@ pub fn RegisterManager(
3838 /// register is free), the value in that slot is undefined.
3939 ///
4040 /// 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,
4242 /// Tracks which registers are free (in which case the
4343 /// corresponding bit is set to 1)
4444 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
......@@ -53,7 +53,7 @@ pub fn RegisterManager(
5353
5454 /// An integer whose bits represent all the registers and
5555 /// 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);
5757 const ShiftInt = math.Log2Int(FreeRegInt);
5858
5959 fn getFunction(self: *Self) *Function {
......@@ -83,14 +83,14 @@ pub fn RegisterManager(
8383 }
8484
8585 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| {
8787 if (reg.id() == cpreg.id()) return i;
8888 }
8989 return null;
9090 }
9191
9292 pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt {
93 return indexOfReg(callee_preserved_regs, reg);
93 return indexOfReg(tracked_registers, reg);
9494 }
9595
9696 /// Returns true when this register is not tracked
......@@ -146,14 +146,14 @@ pub fn RegisterManager(
146146 comptime count: comptime_int,
147147 insts: [count]?Air.Inst.Index,
148148 ) ?[count]Register {
149 comptime assert(count > 0 and count <= callee_preserved_regs.len);
149 comptime assert(count > 0 and count <= tracked_registers.len);
150150
151151 const free_registers = @popCount(FreeRegInt, self.free_registers);
152152 if (free_registers < count) return null;
153153
154154 var regs: [count]Register = undefined;
155155 var i: usize = 0;
156 for (callee_preserved_regs) |reg| {
156 for (tracked_registers) |reg| {
157157 if (i >= count) break;
158158 if (self.isRegFrozen(reg)) continue;
159159 if (self.isRegFree(reg)) {
......@@ -192,8 +192,8 @@ pub fn RegisterManager(
192192 comptime count: comptime_int,
193193 insts: [count]?Air.Inst.Index,
194194 ) AllocateRegistersError![count]Register {
195 comptime assert(count > 0 and count <= callee_preserved_regs.len);
196 if (count > callee_preserved_regs.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters;
195 comptime assert(count > 0 and count <= tracked_registers.len);
196 if (count > tracked_registers.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters;
197197
198198 const result = self.tryAllocRegs(count, insts) orelse blk: {
199199 // We'll take over the first count registers. Spill
......@@ -201,7 +201,7 @@ pub fn RegisterManager(
201201 // stack allocations.
202202 var regs: [count]Register = undefined;
203203 var i: usize = 0;
204 for (callee_preserved_regs) |reg| {
204 for (tracked_registers) |reg| {
205205 if (i >= count) break;
206206 if (self.isRegFrozen(reg)) continue;
207207
......@@ -247,6 +247,7 @@ pub fn RegisterManager(
247247 /// register.
248248 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void {
249249 const index = indexOfRegIntoTracked(reg) orelse return;
250 log.debug("getReg {} for inst {}", .{ reg, inst });
250251 self.markRegAllocated(reg);
251252
252253 if (inst) |tracked_inst|
......@@ -275,6 +276,7 @@ pub fn RegisterManager(
275276 /// spilling is necessary.
276277 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void {
277278 const index = indexOfRegIntoTracked(reg) orelse return;
279 log.debug("getRegAssumeFree {} for inst {}", .{ reg, inst });
278280 self.markRegAllocated(reg);
279281
280282 assert(self.isRegFree(reg));
......@@ -303,7 +305,7 @@ const MockRegister1 = enum(u2) {
303305 return @enumToInt(reg);
304306 }
305307
306 const callee_preserved_regs = [_]MockRegister1{ .r2, .r3 };
308 const allocatable_registers = [_]MockRegister1{ .r2, .r3 };
307309};
308310
309311const MockRegister2 = enum(u2) {
......@@ -316,13 +318,13 @@ const MockRegister2 = enum(u2) {
316318 return @enumToInt(reg);
317319 }
318320
319 const callee_preserved_regs = [_]MockRegister2{ .r0, .r1, .r2, .r3 };
321 const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 };
320322};
321323
322324fn MockFunction(comptime Register: type) type {
323325 return struct {
324326 allocator: Allocator,
325 register_manager: RegisterManager(Self, Register, &Register.callee_preserved_regs) = .{},
327 register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{},
326328 spilled: std.ArrayListUnmanaged(Register) = .{},
327329
328330 const Self = @This();