mirror of
https://github.com/ziglang/zig.git
synced 2026-01-02 19:43:29 +00:00
stage2 ARM: implement caller-saved registers
This commit is contained in:
parent
06058ed6f3
commit
4590e980f7
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
@ -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 };
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user