authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-08 00:01:46-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-04-08 00:01:46-07:00
logab5a445d252ba090d25ae0c49a9b0820ffbb73d3
tree86bc779596dd3080be2a66413f23140b4c708089
parent2d2316f5c0087a610127883f0593e8e9c0e939b7
parent4ff5a3cd94b0532f2cd713082948b00a8a36336f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8459 from joachimschmidt557/stage2-regalloc

Stage2 Register Manager: add getReg function

2 files changed, 115 insertions(+), 15 deletions(-)

src/codegen.zig+26-13
......@@ -1735,7 +1735,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17351735
17361736 switch (result) {
17371737 .register => |reg| {
1738 try self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
1738 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
1739 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
17391740 },
17401741 else => {},
17411742 }
......@@ -1783,8 +1784,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17831784 switch (mc_arg) {
17841785 .none => continue,
17851786 .register => |reg| {
1787 try self.register_manager.getRegWithoutTracking(reg);
17861788 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
1787 // TODO interact with the register allocator to mark the instruction as moved.
17881789 },
17891790 .stack_offset => {
17901791 // Here we need to emit instructions like this:
......@@ -1925,8 +1926,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19251926 .compare_flags_signed => unreachable,
19261927 .compare_flags_unsigned => unreachable,
19271928 .register => |reg| {
1929 try self.register_manager.getRegWithoutTracking(reg);
19281930 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
1929 // TODO interact with the register allocator to mark the instruction as moved.
19301931 },
19311932 .stack_offset => {
19321933 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 {
19881989 .compare_flags_signed => unreachable,
19891990 .compare_flags_unsigned => unreachable,
19901991 .register => |reg| {
1992 try self.register_manager.getRegWithoutTracking(reg);
19911993 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
1992 // TODO interact with the register allocator to mark the instruction as moved.
19931994 },
19941995 .stack_offset => {
19951996 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 {
20392040 switch (mc_arg) {
20402041 .none => continue,
20412042 .register => |reg| {
2043 try self.register_manager.getRegWithoutTracking(reg);
20422044 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2043 // TODO interact with the register allocator to mark the instruction as moved.
20442045 },
20452046 .stack_offset => {
20462047 // Here we need to emit instructions like this:
......@@ -2704,8 +2705,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27042705 const reg_name = input[1 .. input.len - 1];
27052706 const reg = parseRegName(reg_name) orelse
27062707 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
2707 const arg = try self.resolveInst(inst.args[i]);
2708 try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
2708
2709 const arg = inst.args[i];
2710 const arg_mcv = try self.resolveInst(arg);
2711 try self.register_manager.getRegWithoutTracking(reg);
2712 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
27092713 }
27102714
27112715 if (mem.eql(u8, inst.asm_source, "svc #0")) {
......@@ -2734,8 +2738,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27342738 const reg_name = input[1 .. input.len - 1];
27352739 const reg = parseRegName(reg_name) orelse
27362740 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
2737 const arg = try self.resolveInst(inst.args[i]);
2738 try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
2741
2742 const arg = inst.args[i];
2743 const arg_mcv = try self.resolveInst(arg);
2744 try self.register_manager.getRegWithoutTracking(reg);
2745 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
27392746 }
27402747
27412748 if (mem.eql(u8, inst.asm_source, "svc #0")) {
......@@ -2766,8 +2773,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27662773 const reg_name = input[1 .. input.len - 1];
27672774 const reg = parseRegName(reg_name) orelse
27682775 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
2769 const arg = try self.resolveInst(inst.args[i]);
2770 try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
2776
2777 const arg = inst.args[i];
2778 const arg_mcv = try self.resolveInst(arg);
2779 try self.register_manager.getRegWithoutTracking(reg);
2780 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
27712781 }
27722782
27732783 if (mem.eql(u8, inst.asm_source, "ecall")) {
......@@ -2796,8 +2806,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27962806 const reg_name = input[1 .. input.len - 1];
27972807 const reg = parseRegName(reg_name) orelse
27982808 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
2799 const arg = try self.resolveInst(inst.args[i]);
2800 try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg);
2809
2810 const arg = inst.args[i];
2811 const arg_mcv = try self.resolveInst(arg);
2812 try self.register_manager.getRegWithoutTracking(reg);
2813 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
28012814 }
28022815
28032816 if (mem.eql(u8, inst.asm_source, "syscall")) {
src/register_manager.zig+89-2
......@@ -35,6 +35,10 @@ pub fn RegisterManager(
3535 self.registers.deinit(allocator);
3636 }
3737
38 fn isTracked(reg: Register) bool {
39 return std.mem.indexOfScalar(Register, callee_preserved_regs, reg) != null;
40 }
41
3842 fn markRegUsed(self: *Self, reg: Register) void {
3943 if (FreeRegInt == u0) return;
4044 const index = reg.allocIndex() orelse return;
......@@ -51,6 +55,13 @@ pub fn RegisterManager(
5155 self.free_registers |= @as(FreeRegInt, 1) << shift;
5256 }
5357
58 pub fn isRegFree(self: Self, reg: Register) bool {
59 if (FreeRegInt == u0) return true;
60 const index = reg.allocIndex() orelse return true;
61 const shift = @intCast(ShiftInt, index);
62 return self.free_registers & @as(FreeRegInt, 1) << shift != 0;
63 }
64
5465 /// Returns whether this register was allocated in the course
5566 /// of this function
5667 pub fn isRegAllocated(self: Self, reg: Register) bool {
......@@ -117,17 +128,61 @@ pub fn RegisterManager(
117128 const regs_entry = self.registers.remove(reg).?;
118129 const spilled_inst = regs_entry.value;
119130 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
131 self.markRegFree(reg);
120132
121133 break :b reg;
122134 };
123135 }
124136
125 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) !void {
126 try self.registers.putNoClobber(self.getFunction().gpa, reg, inst);
137 /// Allocates the specified register with the specified
138 /// instruction. Spills the register if it is currently
139 /// allocated.
140 /// Before calling, must ensureCapacity + 1 on self.registers.
141 pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void {
142 if (!isTracked(reg)) return;
143
144 if (!self.isRegFree(reg)) {
145 // Move the instruction that was previously there to a
146 // stack allocation.
147 const regs_entry = self.registers.getEntry(reg).?;
148 const spilled_inst = regs_entry.value;
149 regs_entry.value = inst;
150 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
151 } else {
152 self.getRegAssumeFree(reg, inst);
153 }
154 }
155
156 /// Spills the register if it is currently allocated.
157 /// Does not track the register.
158 pub fn getRegWithoutTracking(self: *Self, reg: Register) !void {
159 if (!isTracked(reg)) return;
160
161 if (!self.isRegFree(reg)) {
162 // Move the instruction that was previously there to a
163 // stack allocation.
164 const regs_entry = self.registers.remove(reg).?;
165 const spilled_inst = regs_entry.value;
166 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
167 self.markRegFree(reg);
168 }
169 }
170
171 /// Allocates the specified register with the specified
172 /// instruction. Assumes that the register is free and no
173 /// spilling is necessary.
174 /// Before calling, must ensureCapacity + 1 on self.registers.
175 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void {
176 if (!isTracked(reg)) return;
177
178 self.registers.putAssumeCapacityNoClobber(reg, inst);
127179 self.markRegUsed(reg);
128180 }
129181
182 /// Marks the specified register as free
130183 pub fn freeReg(self: *Self, reg: Register) void {
184 if (!isTracked(reg)) return;
185
131186 _ = self.registers.remove(reg);
132187 self.markRegFree(reg);
133188 }
......@@ -226,3 +281,35 @@ test "allocReg: spilling" {
226281 std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));
227282 std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items);
228283}
284
285test "getReg" {
286 const allocator = std.testing.allocator;
287
288 var function = MockFunction{
289 .allocator = allocator,
290 };
291 defer function.deinit();
292
293 var mock_instruction = ir.Inst{
294 .tag = .breakpoint,
295 .ty = Type.initTag(.void),
296 .src = .unneeded,
297 };
298
299 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
300 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
301
302 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
303 try function.register_manager.getReg(.r3, &mock_instruction);
304
305 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
306 std.testing.expect(function.register_manager.isRegAllocated(.r3));
307
308 // Spill r3
309 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
310 try function.register_manager.getReg(.r3, &mock_instruction);
311
312 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
313 std.testing.expect(function.register_manager.isRegAllocated(.r3));
314 std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r3}, function.spilled.items);
315}