diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index d6bda85377..2248daf1d0 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -22,7 +22,7 @@ const leb128 = std.leb; const log = std.log.scoped(.codegen); const build_options = @import("build_options"); const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); +const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); const FnResult = @import("../../codegen.zig").FnResult; const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; @@ -34,6 +34,8 @@ const Register = bits.Register; const Instruction = bits.Instruction; const Condition = bits.Condition; const callee_preserved_regs = abi.callee_preserved_regs; +const caller_preserved_regs = abi.caller_preserved_regs; +const allocatable_registers = abi.allocatable_registers; const c_abi_int_param_regs = abi.c_abi_int_param_regs; const c_abi_int_return_regs = abi.c_abi_int_return_regs; @@ -788,10 +790,6 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { if (!elem_ty.hasRuntimeBits()) { // As this stack item will never be dereferenced at runtime, // return the current stack offset - try self.stack.putNoClobber(self.gpa, self.next_stack_offset, .{ - .inst = inst, - .size = 0, - }); return self.next_stack_offset; } @@ -1569,13 +1567,13 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind switch (mcv) { .register => |reg| { - // If it's in the registers table, need to associate the register with the - // new instruction. - if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { - if (!self.register_manager.isRegFree(reg)) { - self.register_manager.registers[index] = inst; - } + // We assert that this register is allocatable by asking + // for its index + const index = RegisterManager.indexOfRegIntoTracked(reg).?; // see note above + if (!self.register_manager.isRegFree(reg)) { + self.register_manager.registers[index] = inst; } + log.debug("%{d} => {} (reused)", .{ inst, reg }); }, .stack_offset => |off| { @@ -2545,13 +2543,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. // Architecture, compare flags are not preserved across // calls. Therefore, if some value is currently stored there, we // need to save it. - // - // TODO once caller-saved registers are implemented, save them - // here too, but crucially *after* we save the compare flags as - // saving compare flags may require a new caller-saved register try self.spillCompareFlagsIfOccupied(); + // Save caller-saved registers, but crucially *after* we save the + // compare flags as saving compare flags may require a new + // caller-saved register + for (caller_preserved_regs) |reg| { + try self.register_manager.getReg(reg, null); + } + if (info.return_value == .stack_offset) { + log.debug("airCall: return by reference", .{}); const ret_ty = fn_ty.fnReturnType(); const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); 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. .data = ret_ty, }; const ptr_ty = Type.initPayload(&ptr_ty_payload.base); - try self.register_manager.getReg(.r0, inst); + try self.register_manager.getReg(.r0, null); try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset }); info.return_value = .{ .stack_offset = stack_offset }; @@ -2662,8 +2664,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const result: MCValue = result: { switch (info.return_value) { .register => |reg| { - if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { - // Save function return value in a callee saved register + if (RegisterManager.indexOfRegIntoTracked(reg) == null) { + // Save function return value into a tracked register + log.debug("airCall: copying {} as it is not tracked", .{reg}); break :result try self.copyToNewRegister(inst, info.return_value); } }, diff --git a/src/arch/arm/abi.zig b/src/arch/arm/abi.zig index a5a84e8f8b..4073b92222 100644 --- a/src/arch/arm/abi.zig +++ b/src/arch/arm/abi.zig @@ -2,5 +2,8 @@ const bits = @import("bits.zig"); const Register = bits.Register; pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; +pub const caller_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3 }; +pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs; + pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; diff --git a/src/register_manager.zig b/src/register_manager.zig index a29cfbf921..7adf101967 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -26,11 +26,11 @@ pub const AllocateRegistersError = error{ pub fn RegisterManager( comptime Function: type, comptime Register: type, - comptime callee_preserved_regs: []const Register, + comptime tracked_registers: []const Register, ) type { // architectures which do not have a concept of registers should // refrain from using RegisterManager - assert(callee_preserved_regs.len > 0); // see note above + assert(tracked_registers.len > 0); // see note above return struct { /// Tracks the AIR instruction allocated to every register. If @@ -38,7 +38,7 @@ pub fn RegisterManager( /// register is free), the value in that slot is undefined. /// /// The key must be canonical register. - registers: [callee_preserved_regs.len]Air.Inst.Index = undefined, + registers: [tracked_registers.len]Air.Inst.Index = undefined, /// Tracks which registers are free (in which case the /// corresponding bit is set to 1) free_registers: FreeRegInt = math.maxInt(FreeRegInt), @@ -53,7 +53,7 @@ pub fn RegisterManager( /// An integer whose bits represent all the registers and /// whether they are free. - const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len); + const FreeRegInt = std.meta.Int(.unsigned, tracked_registers.len); const ShiftInt = math.Log2Int(FreeRegInt); fn getFunction(self: *Self) *Function { @@ -83,14 +83,14 @@ pub fn RegisterManager( } pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) { - inline for (callee_preserved_regs) |cpreg, i| { + inline for (tracked_registers) |cpreg, i| { if (reg.id() == cpreg.id()) return i; } return null; } pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt { - return indexOfReg(callee_preserved_regs, reg); + return indexOfReg(tracked_registers, reg); } /// Returns true when this register is not tracked @@ -146,14 +146,14 @@ pub fn RegisterManager( comptime count: comptime_int, insts: [count]?Air.Inst.Index, ) ?[count]Register { - comptime assert(count > 0 and count <= callee_preserved_regs.len); + comptime assert(count > 0 and count <= tracked_registers.len); const free_registers = @popCount(FreeRegInt, self.free_registers); if (free_registers < count) return null; var regs: [count]Register = undefined; var i: usize = 0; - for (callee_preserved_regs) |reg| { + for (tracked_registers) |reg| { if (i >= count) break; if (self.isRegFrozen(reg)) continue; if (self.isRegFree(reg)) { @@ -192,8 +192,8 @@ pub fn RegisterManager( comptime count: comptime_int, insts: [count]?Air.Inst.Index, ) AllocateRegistersError![count]Register { - comptime assert(count > 0 and count <= callee_preserved_regs.len); - if (count > callee_preserved_regs.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters; + comptime assert(count > 0 and count <= tracked_registers.len); + if (count > tracked_registers.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters; const result = self.tryAllocRegs(count, insts) orelse blk: { // We'll take over the first count registers. Spill @@ -201,7 +201,7 @@ pub fn RegisterManager( // stack allocations. var regs: [count]Register = undefined; var i: usize = 0; - for (callee_preserved_regs) |reg| { + for (tracked_registers) |reg| { if (i >= count) break; if (self.isRegFrozen(reg)) continue; @@ -247,6 +247,7 @@ pub fn RegisterManager( /// register. pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void { const index = indexOfRegIntoTracked(reg) orelse return; + log.debug("getReg {} for inst {}", .{ reg, inst }); self.markRegAllocated(reg); if (inst) |tracked_inst| @@ -275,6 +276,7 @@ pub fn RegisterManager( /// spilling is necessary. pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void { const index = indexOfRegIntoTracked(reg) orelse return; + log.debug("getRegAssumeFree {} for inst {}", .{ reg, inst }); self.markRegAllocated(reg); assert(self.isRegFree(reg)); @@ -303,7 +305,7 @@ const MockRegister1 = enum(u2) { return @enumToInt(reg); } - const callee_preserved_regs = [_]MockRegister1{ .r2, .r3 }; + const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; }; const MockRegister2 = enum(u2) { @@ -316,13 +318,13 @@ const MockRegister2 = enum(u2) { return @enumToInt(reg); } - const callee_preserved_regs = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; + const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; }; fn MockFunction(comptime Register: type) type { return struct { allocator: Allocator, - register_manager: RegisterManager(Self, Register, &Register.callee_preserved_regs) = .{}, + register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, spilled: std.ArrayListUnmanaged(Register) = .{}, const Self = @This();