x64: use register classes mask to select between gp and avx

This commit is contained in:
Jakub Konka 2022-05-18 12:23:07 +02:00
parent 549174f743
commit f346150820
4 changed files with 81 additions and 39 deletions

View File

@ -38,6 +38,9 @@ const c_abi_int_return_regs = abi.c_abi_int_return_regs;
const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers);
const RegisterLock = RegisterManager.RegisterLock;
const Register = bits.Register;
const RegisterClass = abi.RegisterClass;
const gp = RegisterClass.gp;
const avx = RegisterClass.avx;
const InnerError = error{
OutOfMemory,
@ -882,7 +885,9 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
// TODO check if AVX available
const ptr_bytes: u64 = 32;
if (abi_size <= ptr_bytes) {
if (self.register_manager.tryAllocReg(inst, .{})) |reg| {
if (self.register_manager.tryAllocReg(inst, .{
.selector_mask = avx,
})) |reg| {
return MCValue{ .register = registerAlias(reg, abi_size) };
}
}
@ -892,7 +897,9 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
if (abi_size <= ptr_bytes) {
if (self.register_manager.tryAllocReg(inst, .{})) |reg| {
if (self.register_manager.tryAllocReg(inst, .{
.selector_mask = gp,
})) |reg| {
return MCValue{ .register = registerAlias(reg, abi_size) };
}
}
@ -963,7 +970,13 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou
/// allocated. A second call to `copyToTmpRegister` may return the same register.
/// This can have a side effect of spilling instructions to the stack to free up a register.
fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
const reg = try self.register_manager.allocReg(null, .{});
const mask = switch (ty.zigTypeTag()) {
.Float => avx,
else => gp,
};
const reg: Register = try self.register_manager.allocReg(null, .{
.selector_mask = mask,
});
try self.genSetReg(ty, reg, mcv);
return reg;
}
@ -973,7 +986,13 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
/// This can have a side effect of spilling instructions to the stack to free up a register.
/// WARNING make sure that the allocated register matches the returned MCValue from an instruction!
fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue {
const reg = try self.register_manager.allocReg(reg_owner, .{});
const mask = switch (ty.zigTypeTag()) {
.Float => avx,
else => gp,
};
const reg: Register = try self.register_manager.allocReg(reg_owner, .{
.selector_mask = mask,
});
try self.genSetReg(ty, reg, mcv);
return MCValue{ .register = reg };
}
@ -1029,7 +1048,9 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
};
defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
const reg = try self.register_manager.allocReg(inst, .{});
const reg = try self.register_manager.allocReg(inst, .{
.selector_mask = gp,
});
try self.genSetReg(dest_ty, reg, .{ .immediate = 0 });
try self.genSetReg(operand_ty, reg, operand);
break :blk MCValue{ .register = reg };
@ -1384,7 +1405,9 @@ fn genSetStackTruncatedOverflowCompare(
.unsigned => ty,
};
const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }, .{});
const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null }, .{
.selector_mask = gp,
});
const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs);
defer for (temp_regs_locks) |rreg| {
self.register_manager.unlockReg(rreg);
@ -2046,7 +2069,9 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
defer self.register_manager.unlockReg(offset_reg_lock);
const addr_reg = try self.register_manager.allocReg(null, .{});
const addr_reg = try self.register_manager.allocReg(null, .{
.selector_mask = gp,
});
switch (slice_mcv) {
.stack_offset => |off| {
// mov reg, [rbp - 8]
@ -2125,7 +2150,9 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
defer self.register_manager.unlockReg(offset_reg_lock);
const addr_reg = try self.register_manager.allocReg(null, .{});
const addr_reg = try self.register_manager.allocReg(null, .{
.selector_mask = gp,
});
switch (array) {
.register => {
const off = @intCast(i32, try self.allocMem(
@ -2492,7 +2519,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
},
.stack_offset => |off| {
if (abi_size <= 8) {
const tmp_reg = try self.register_manager.allocReg(null, .{});
const tmp_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
try self.load(.{ .register = tmp_reg }, ptr, ptr_ty);
return self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }, .{});
}
@ -2693,7 +2720,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
};
defer if (value_lock) |lock| self.register_manager.unlockReg(lock);
const addr_reg = try self.register_manager.allocReg(null, .{});
const addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
defer self.register_manager.unlockReg(addr_reg_lock);
@ -2765,7 +2792,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
.memory,
=> {
if (abi_size <= 8) {
const tmp_reg = try self.register_manager.allocReg(null, .{});
const tmp_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
defer self.register_manager.unlockReg(tmp_reg_lock);
@ -2883,7 +2910,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
if (can_reuse_operand) {
break :blk reg;
} else {
const result_reg = try self.register_manager.allocReg(inst, .{});
const result_reg = try self.register_manager.allocReg(inst, .{ .selector_mask = gp });
try self.genSetReg(ptr_ty, result_reg, mcv);
break :blk result_reg;
}
@ -2984,7 +3011,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
defer self.register_manager.unlockReg(reg_lock);
const dst_reg = try self.register_manager.allocReg(inst, .{});
const dst_reg = try self.register_manager.allocReg(inst, .{ .selector_mask = gp });
const flags: u2 = switch (mcv) {
.register_overflow_unsigned => 0b10,
.register_overflow_signed => 0b00,
@ -5362,7 +5389,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
const overflow_bit_ty = ty.structFieldType(1);
const overflow_bit_offset = ty.structFieldOffset(1, self.target.*);
const tmp_reg = try self.register_manager.allocReg(null, .{});
const tmp_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
const flags: u2 = switch (mcv) {
.register_overflow_unsigned => 0b10,
.register_overflow_signed => 0b00,
@ -5580,7 +5607,7 @@ fn genInlineMemcpy(
null;
defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock);
const dst_addr_reg = try self.register_manager.allocReg(null, .{});
const dst_addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
switch (dst_ptr) {
.memory,
.got_load,
@ -5615,7 +5642,7 @@ fn genInlineMemcpy(
const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg);
defer self.register_manager.unlockReg(dst_addr_reg_lock);
const src_addr_reg = try self.register_manager.allocReg(null, .{});
const src_addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
switch (src_ptr) {
.memory,
.got_load,
@ -5650,7 +5677,9 @@ fn genInlineMemcpy(
const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg);
defer self.register_manager.unlockReg(src_addr_reg_lock);
const regs = try self.register_manager.allocRegs(2, .{ null, null }, .{});
const regs = try self.register_manager.allocRegs(2, .{ null, null }, .{
.selector_mask = gp,
});
const count_reg = regs[0].to64();
const tmp_reg = regs[1].to8();
@ -5750,7 +5779,7 @@ fn genInlineMemset(
const rax_lock = self.register_manager.lockRegAssumeUnused(.rax);
defer self.register_manager.unlockReg(rax_lock);
const addr_reg = try self.register_manager.allocReg(null, .{});
const addr_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
switch (dst_ptr) {
.memory,
.got_load,
@ -6018,7 +6047,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
switch (ty.tag()) {
.f32 => return self.fail("TODO genSetReg from memory for f32", .{}),
.f64 => {
const base_reg = try self.register_manager.allocReg(null, .{});
const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);
_ = try self.addInst(.{
.tag = .mov_f64,
@ -6328,7 +6357,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
const src: MCValue = blk: {
switch (src_ptr) {
.got_load, .direct_load, .memory => {
const reg = try self.register_manager.allocReg(null, .{});
const reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
try self.loadMemPtrIntoRegister(reg, src_ty, src_ptr);
_ = try self.addInst(.{
.tag = .mov,

View File

@ -384,11 +384,12 @@ pub const avx_regs = [_]Register{
};
pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ avx_regs;
// Masks for register manager
const FreeRegInt = std.meta.Int(.unsigned, allocatable_registers.len);
// TODO
pub const gp_mask: FreeRegInt = 0x3fff;
pub const avx_mask: FreeRegInt = 0x3fff_c000;
pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
// Masks for register manager
const FreeRegInt = std.meta.Int(.unsigned, allocatable_registers.len);
pub const RegisterClass = struct {
pub const gp: FreeRegInt = 0x3fff;
pub const avx: FreeRegInt = 0x3fff_c000;
};

View File

@ -56,10 +56,10 @@ pub const Register = enum(u7) {
// Pseudo-value for MIR instructions.
none,
pub fn id(self: Register) u5 {
pub fn id(self: Register) u7 {
return switch (@enumToInt(self)) {
0...63 => @as(u5, @truncate(u4, @enumToInt(self))),
64...79 => @truncate(u5, @enumToInt(self)),
0...63 => @as(u7, @truncate(u4, @enumToInt(self))),
64...79 => @enumToInt(self),
else => unreachable,
};
}
@ -101,31 +101,31 @@ pub const Register = enum(u7) {
}
pub fn to256(self: Register) Register {
return @intToEnum(Register, @as(u8, self.id()) + 64);
return @intToEnum(Register, @as(u8, self.enc()) + 64);
}
pub fn to128(self: Register) Register {
return @intToEnum(Register, @as(u8, self.id()) + 80);
return @intToEnum(Register, @as(u8, self.enc()) + 80);
}
/// Convert from any register to its 64 bit alias.
pub fn to64(self: Register) Register {
return @intToEnum(Register, self.id());
return @intToEnum(Register, self.enc());
}
/// Convert from any register to its 32 bit alias.
pub fn to32(self: Register) Register {
return @intToEnum(Register, @as(u8, self.id()) + 16);
return @intToEnum(Register, @as(u8, self.enc()) + 16);
}
/// Convert from any register to its 16 bit alias.
pub fn to16(self: Register) Register {
return @intToEnum(Register, @as(u8, self.id()) + 32);
return @intToEnum(Register, @as(u8, self.enc()) + 32);
}
/// Convert from any register to its 8 bit alias.
pub fn to8(self: Register) Register {
return @intToEnum(Register, @as(u8, self.id()) + 48);
return @intToEnum(Register, @as(u8, self.enc()) + 48);
}
pub fn dwarfLocOp(self: Register) u8 {

View File

@ -66,6 +66,11 @@ pub fn RegisterManager(
return mask;
}
fn excludeRegister(reg: Register, mask: FreeRegInt) bool {
const reg_mask = getRegisterMask(reg) orelse return true;
return reg_mask & mask == 0;
}
fn markRegAllocated(self: *Self, reg: Register) void {
const mask = getRegisterMask(reg) orelse return;
self.allocated_registers |= mask;
@ -186,10 +191,11 @@ pub fn RegisterManager(
insts: [count]?Air.Inst.Index,
opts: AllocOpts,
) ?[count]Register {
_ = opts;
comptime assert(count > 0 and count <= tracked_registers.len);
const free_and_not_locked_registers = self.free_registers & ~self.locked_registers;
const selector_mask = if (opts.selector_mask) |mask| mask else ~@as(FreeRegInt, 0);
const free_registers = self.free_registers & selector_mask;
const free_and_not_locked_registers = free_registers & ~self.locked_registers;
const free_and_not_locked_registers_count = @popCount(FreeRegInt, free_and_not_locked_registers);
if (free_and_not_locked_registers_count < count) return null;
@ -197,6 +203,7 @@ pub fn RegisterManager(
var i: usize = 0;
for (tracked_registers) |reg| {
if (i >= count) break;
if (excludeRegister(reg, selector_mask)) continue;
if (self.isRegLocked(reg)) continue;
if (!self.isRegFree(reg)) continue;
@ -236,8 +243,12 @@ pub fn RegisterManager(
opts: AllocOpts,
) AllocateRegistersError![count]Register {
comptime assert(count > 0 and count <= tracked_registers.len);
const locked_registers_count = @popCount(FreeRegInt, self.locked_registers);
if (count > tracked_registers.len - locked_registers_count) return error.OutOfRegisters;
const selector_mask = if (opts.selector_mask) |mask| mask else ~@as(FreeRegInt, 0);
const available_registers_count = @popCount(FreeRegInt, selector_mask);
const locked_registers = self.locked_registers & selector_mask;
const locked_registers_count = @popCount(FreeRegInt, locked_registers);
if (count > available_registers_count - locked_registers_count) return error.OutOfRegisters;
const result = self.tryAllocRegs(count, insts, opts) orelse blk: {
// We'll take over the first count registers. Spill
@ -247,6 +258,7 @@ pub fn RegisterManager(
var i: usize = 0;
for (tracked_registers) |reg| {
if (i >= count) break;
if (excludeRegister(reg, selector_mask)) continue;
if (self.isRegLocked(reg)) continue;
regs[i] = reg;