Merge pull request #8459 from joachimschmidt557/stage2-regalloc

Stage2 Register Manager: add getReg function
This commit is contained in:
Andrew Kelley 2021-04-08 00:01:46 -07:00 committed by GitHub
commit ab5a445d25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 115 additions and 15 deletions

View File

@ -1735,7 +1735,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (result) {
.register => |reg| {
try self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
},
else => {},
}
@ -1783,8 +1784,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (mc_arg) {
.none => continue,
.register => |reg| {
try self.register_manager.getRegWithoutTracking(reg);
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
// TODO interact with the register allocator to mark the instruction as moved.
},
.stack_offset => {
// Here we need to emit instructions like this:
@ -1925,8 +1926,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.compare_flags_signed => unreachable,
.compare_flags_unsigned => unreachable,
.register => |reg| {
try self.register_manager.getRegWithoutTracking(reg);
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
// TODO interact with the register allocator to mark the instruction as moved.
},
.stack_offset => {
return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
@ -1988,8 +1989,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.compare_flags_signed => unreachable,
.compare_flags_unsigned => unreachable,
.register => |reg| {
try self.register_manager.getRegWithoutTracking(reg);
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
// TODO interact with the register allocator to mark the instruction as moved.
},
.stack_offset => {
return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
@ -2039,8 +2040,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
switch (mc_arg) {
.none => continue,
.register => |reg| {
try self.register_manager.getRegWithoutTracking(reg);
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
// TODO interact with the register allocator to mark the instruction as moved.
},
.stack_offset => {
// Here we need to emit instructions like this:
@ -2704,8 +2705,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const reg_name = input[1 .. input.len - 1];
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const arg = try self.resolveInst(inst.args[i]);
try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
const arg = inst.args[i];
const arg_mcv = try self.resolveInst(arg);
try self.register_manager.getRegWithoutTracking(reg);
try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
}
if (mem.eql(u8, inst.asm_source, "svc #0")) {
@ -2734,8 +2738,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const reg_name = input[1 .. input.len - 1];
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const arg = try self.resolveInst(inst.args[i]);
try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
const arg = inst.args[i];
const arg_mcv = try self.resolveInst(arg);
try self.register_manager.getRegWithoutTracking(reg);
try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
}
if (mem.eql(u8, inst.asm_source, "svc #0")) {
@ -2766,8 +2773,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const reg_name = input[1 .. input.len - 1];
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const arg = try self.resolveInst(inst.args[i]);
try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
const arg = inst.args[i];
const arg_mcv = try self.resolveInst(arg);
try self.register_manager.getRegWithoutTracking(reg);
try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
}
if (mem.eql(u8, inst.asm_source, "ecall")) {
@ -2796,8 +2806,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const reg_name = input[1 .. input.len - 1];
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const arg = try self.resolveInst(inst.args[i]);
try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
const arg = inst.args[i];
const arg_mcv = try self.resolveInst(arg);
try self.register_manager.getRegWithoutTracking(reg);
try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
}
if (mem.eql(u8, inst.asm_source, "syscall")) {

View File

@ -35,6 +35,10 @@ pub fn RegisterManager(
self.registers.deinit(allocator);
}
fn isTracked(reg: Register) bool {
return std.mem.indexOfScalar(Register, callee_preserved_regs, reg) != null;
}
fn markRegUsed(self: *Self, reg: Register) void {
if (FreeRegInt == u0) return;
const index = reg.allocIndex() orelse return;
@ -51,6 +55,13 @@ pub fn RegisterManager(
self.free_registers |= @as(FreeRegInt, 1) << shift;
}
pub fn isRegFree(self: Self, reg: Register) bool {
if (FreeRegInt == u0) return true;
const index = reg.allocIndex() orelse return true;
const shift = @intCast(ShiftInt, index);
return self.free_registers & @as(FreeRegInt, 1) << shift != 0;
}
/// Returns whether this register was allocated in the course
/// of this function
pub fn isRegAllocated(self: Self, reg: Register) bool {
@ -117,17 +128,61 @@ pub fn RegisterManager(
const regs_entry = self.registers.remove(reg).?;
const spilled_inst = regs_entry.value;
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
self.markRegFree(reg);
break :b reg;
};
}
pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) !void {
try self.registers.putNoClobber(self.getFunction().gpa, reg, inst);
/// Allocates the specified register with the specified
/// instruction. Spills the register if it is currently
/// allocated.
/// Before calling, must ensureCapacity + 1 on self.registers.
pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void {
if (!isTracked(reg)) return;
if (!self.isRegFree(reg)) {
// Move the instruction that was previously there to a
// stack allocation.
const regs_entry = self.registers.getEntry(reg).?;
const spilled_inst = regs_entry.value;
regs_entry.value = inst;
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
} else {
self.getRegAssumeFree(reg, inst);
}
}
/// Spills the register if it is currently allocated.
/// Does not track the register.
pub fn getRegWithoutTracking(self: *Self, reg: Register) !void {
if (!isTracked(reg)) return;
if (!self.isRegFree(reg)) {
// Move the instruction that was previously there to a
// stack allocation.
const regs_entry = self.registers.remove(reg).?;
const spilled_inst = regs_entry.value;
try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
self.markRegFree(reg);
}
}
/// Allocates the specified register with the specified
/// instruction. Assumes that the register is free and no
/// spilling is necessary.
/// Before calling, must ensureCapacity + 1 on self.registers.
pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void {
if (!isTracked(reg)) return;
self.registers.putAssumeCapacityNoClobber(reg, inst);
self.markRegUsed(reg);
}
/// Marks the specified register as free
pub fn freeReg(self: *Self, reg: Register) void {
if (!isTracked(reg)) return;
_ = self.registers.remove(reg);
self.markRegFree(reg);
}
@ -226,3 +281,35 @@ test "allocReg: spilling" {
std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));
std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items);
}
test "getReg" {
const allocator = std.testing.allocator;
var function = MockFunction{
.allocator = allocator,
};
defer function.deinit();
var mock_instruction = ir.Inst{
.tag = .breakpoint,
.ty = Type.initTag(.void),
.src = .unneeded,
};
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
std.testing.expect(!function.register_manager.isRegAllocated(.r3));
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
try function.register_manager.getReg(.r3, &mock_instruction);
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
std.testing.expect(function.register_manager.isRegAllocated(.r3));
// Spill r3
try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
try function.register_manager.getReg(.r3, &mock_instruction);
std.testing.expect(!function.register_manager.isRegAllocated(.r2));
std.testing.expect(function.register_manager.isRegAllocated(.r3));
std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r3}, function.spilled.items);
}