diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen.zig | 39 | ||||
| -rw-r--r-- | src/register_manager.zig | 91 |
2 files changed, 115 insertions, 15 deletions
diff --git a/src/codegen.zig b/src/codegen.zig index fbd412ceba..220a8fa374 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -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")) { diff --git a/src/register_manager.zig b/src/register_manager.zig index abb1632165..e11f2c3111 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -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); +} |
