Commit 4590e980f7
Changed files (3)
src
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 };
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);
}
},
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();