| author | |
| committer | |
| log | ab5a445d252ba090d25ae0c49a9b0820ffbb73d3 |
| tree | 86bc779596dd3080be2a66413f23140b4c708089 |
| parent | 2d2316f5c0087a610127883f0593e8e9c0e939b7 |
| parent | 4ff5a3cd94b0532f2cd713082948b00a8a36336f |
| signature |
Stage2 Register Manager: add getReg function2 files changed, 115 insertions(+), 15 deletions(-)
src/codegen.zig+26-13| ... | ... | @@ -1735,7 +1735,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1735 | 1735 | |
| 1736 | 1736 | switch (result) { |
| 1737 | 1737 | .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); | |
| 1739 | 1740 | }, |
| 1740 | 1741 | else => {}, |
| 1741 | 1742 | } |
| ... | ... | @@ -1783,8 +1784,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1783 | 1784 | switch (mc_arg) { |
| 1784 | 1785 | .none => continue, |
| 1785 | 1786 | .register => |reg| { |
| 1787 | try self.register_manager.getRegWithoutTracking(reg); | |
| 1786 | 1788 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 1787 | // TODO interact with the register allocator to mark the instruction as moved. | |
| 1788 | 1789 | }, |
| 1789 | 1790 | .stack_offset => { |
| 1790 | 1791 | // Here we need to emit instructions like this: |
| ... | ... | @@ -1925,8 +1926,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1925 | 1926 | .compare_flags_signed => unreachable, |
| 1926 | 1927 | .compare_flags_unsigned => unreachable, |
| 1927 | 1928 | .register => |reg| { |
| 1929 | try self.register_manager.getRegWithoutTracking(reg); | |
| 1928 | 1930 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 1929 | // TODO interact with the register allocator to mark the instruction as moved. | |
| 1930 | 1931 | }, |
| 1931 | 1932 | .stack_offset => { |
| 1932 | 1933 | 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 { |
| 1988 | 1989 | .compare_flags_signed => unreachable, |
| 1989 | 1990 | .compare_flags_unsigned => unreachable, |
| 1990 | 1991 | .register => |reg| { |
| 1992 | try self.register_manager.getRegWithoutTracking(reg); | |
| 1991 | 1993 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 1992 | // TODO interact with the register allocator to mark the instruction as moved. | |
| 1993 | 1994 | }, |
| 1994 | 1995 | .stack_offset => { |
| 1995 | 1996 | 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 { |
| 2039 | 2040 | switch (mc_arg) { |
| 2040 | 2041 | .none => continue, |
| 2041 | 2042 | .register => |reg| { |
| 2043 | try self.register_manager.getRegWithoutTracking(reg); | |
| 2042 | 2044 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 2043 | // TODO interact with the register allocator to mark the instruction as moved. | |
| 2044 | 2045 | }, |
| 2045 | 2046 | .stack_offset => { |
| 2046 | 2047 | // Here we need to emit instructions like this: |
| ... | ... | @@ -2704,8 +2705,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2704 | 2705 | const reg_name = input[1 .. input.len - 1]; |
| 2705 | 2706 | const reg = parseRegName(reg_name) orelse |
| 2706 | 2707 | 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); | |
| 2709 | 2713 | } |
| 2710 | 2714 | |
| 2711 | 2715 | if (mem.eql(u8, inst.asm_source, "svc #0")) { |
| ... | ... | @@ -2734,8 +2738,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2734 | 2738 | const reg_name = input[1 .. input.len - 1]; |
| 2735 | 2739 | const reg = parseRegName(reg_name) orelse |
| 2736 | 2740 | 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); | |
| 2739 | 2746 | } |
| 2740 | 2747 | |
| 2741 | 2748 | if (mem.eql(u8, inst.asm_source, "svc #0")) { |
| ... | ... | @@ -2766,8 +2773,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2766 | 2773 | const reg_name = input[1 .. input.len - 1]; |
| 2767 | 2774 | const reg = parseRegName(reg_name) orelse |
| 2768 | 2775 | 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); | |
| 2771 | 2781 | } |
| 2772 | 2782 | |
| 2773 | 2783 | if (mem.eql(u8, inst.asm_source, "ecall")) { |
| ... | ... | @@ -2796,8 +2806,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2796 | 2806 | const reg_name = input[1 .. input.len - 1]; |
| 2797 | 2807 | const reg = parseRegName(reg_name) orelse |
| 2798 | 2808 | 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); | |
| 2801 | 2814 | } |
| 2802 | 2815 | |
| 2803 | 2816 | if (mem.eql(u8, inst.asm_source, "syscall")) { |
src/register_manager.zig+89-2| ... | ... | @@ -35,6 +35,10 @@ pub fn RegisterManager( |
| 35 | 35 | self.registers.deinit(allocator); |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | fn isTracked(reg: Register) bool { | |
| 39 | return std.mem.indexOfScalar(Register, callee_preserved_regs, reg) != null; | |
| 40 | } | |
| 41 | ||
| 38 | 42 | fn markRegUsed(self: *Self, reg: Register) void { |
| 39 | 43 | if (FreeRegInt == u0) return; |
| 40 | 44 | const index = reg.allocIndex() orelse return; |
| ... | ... | @@ -51,6 +55,13 @@ pub fn RegisterManager( |
| 51 | 55 | self.free_registers |= @as(FreeRegInt, 1) << shift; |
| 52 | 56 | } |
| 53 | 57 | |
| 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 | ||
| 54 | 65 | /// Returns whether this register was allocated in the course |
| 55 | 66 | /// of this function |
| 56 | 67 | pub fn isRegAllocated(self: Self, reg: Register) bool { |
| ... | ... | @@ -117,17 +128,61 @@ pub fn RegisterManager( |
| 117 | 128 | const regs_entry = self.registers.remove(reg).?; |
| 118 | 129 | const spilled_inst = regs_entry.value; |
| 119 | 130 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 131 | self.markRegFree(reg); | |
| 120 | 132 | |
| 121 | 133 | break :b reg; |
| 122 | 134 | }; |
| 123 | 135 | } |
| 124 | 136 | |
| 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); | |
| 127 | 179 | self.markRegUsed(reg); |
| 128 | 180 | } |
| 129 | 181 | |
| 182 | /// Marks the specified register as free | |
| 130 | 183 | pub fn freeReg(self: *Self, reg: Register) void { |
| 184 | if (!isTracked(reg)) return; | |
| 185 | ||
| 131 | 186 | _ = self.registers.remove(reg); |
| 132 | 187 | self.markRegFree(reg); |
| 133 | 188 | } |
| ... | ... | @@ -226,3 +281,35 @@ test "allocReg: spilling" { |
| 226 | 281 | std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction)); |
| 227 | 282 | std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); |
| 228 | 283 | } |
| 284 | ||
| 285 | test "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 | } |