| ... | @@ -16,7 +16,7 @@ pub fn RegisterManager( | ... | @@ -16,7 +16,7 @@ pub fn RegisterManager( |
| 16 | ) type { | 16 | ) type { |
| 17 | return struct { | 17 | return struct { |
| 18 | /// The key must be canonical register. | 18 | /// The key must be canonical register. |
| 19 | registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{}, | 19 | registers: [callee_preserved_regs.len]?*ir.Inst = [_]?*ir.Inst{null} ** callee_preserved_regs.len, |
| 20 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), | 20 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), |
| 21 | /// Tracks all registers allocated in the course of this function | 21 | /// Tracks all registers allocated in the course of this function |
| 22 | allocated_registers: FreeRegInt = 0, | 22 | allocated_registers: FreeRegInt = 0, |
| ... | @@ -31,14 +31,6 @@ pub fn RegisterManager( | ... | @@ -31,14 +31,6 @@ pub fn RegisterManager( |
| 31 | return @fieldParentPtr(Function, "register_manager", self); | 31 | return @fieldParentPtr(Function, "register_manager", self); |
| 32 | } | 32 | } |
| 33 | | 33 | |
| 34 | pub fn deinit(self: *Self, allocator: *Allocator) void { | | |
| 35 | self.registers.deinit(allocator); | | |
| 36 | } | | |
| 37 | | | |
| 38 | fn isTracked(reg: Register) bool { | | |
| 39 | return reg.allocIndex() != null; | | |
| 40 | } | | |
| 41 | | | |
| 42 | fn markRegUsed(self: *Self, reg: Register) void { | 34 | fn markRegUsed(self: *Self, reg: Register) void { |
| 43 | if (FreeRegInt == u0) return; | 35 | if (FreeRegInt == u0) return; |
| 44 | const index = reg.allocIndex() orelse return; | 36 | const index = reg.allocIndex() orelse return; |
| ... | @@ -73,13 +65,13 @@ pub fn RegisterManager( | ... | @@ -73,13 +65,13 @@ pub fn RegisterManager( |
| 73 | return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; | 65 | return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; |
| 74 | } | 66 | } |
| 75 | | 67 | |
| 76 | /// Before calling, must ensureCapacity + count on self.registers. | | |
| 77 | /// Returns `null` if all registers are allocated. | 68 | /// Returns `null` if all registers are allocated. |
| 78 | pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register { | 69 | pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register { |
| 79 | if (self.tryAllocRegsWithoutTracking(count)) |regs| { | 70 | if (self.tryAllocRegsWithoutTracking(count)) |regs| { |
| 80 | for (regs) |reg, i| { | 71 | for (regs) |reg, i| { |
| | 72 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null |
| | 73 | self.registers[index] = insts[i]; |
| 81 | self.markRegUsed(reg); | 74 | self.markRegUsed(reg); |
| 82 | self.registers.putAssumeCapacityNoClobber(reg, insts[i]); | | |
| 83 | } | 75 | } |
| 84 | | 76 | |
| 85 | return regs; | 77 | return regs; |
| ... | @@ -88,13 +80,11 @@ pub fn RegisterManager( | ... | @@ -88,13 +80,11 @@ pub fn RegisterManager( |
| 88 | } | 80 | } |
| 89 | } | 81 | } |
| 90 | | 82 | |
| 91 | /// Before calling, must ensureCapacity + 1 on self.registers. | | |
| 92 | /// Returns `null` if all registers are allocated. | 83 | /// Returns `null` if all registers are allocated. |
| 93 | pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register { | 84 | pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register { |
| 94 | return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null; | 85 | return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null; |
| 95 | } | 86 | } |
| 96 | | 87 | |
| 97 | /// Before calling, must ensureCapacity + count on self.registers. | | |
| 98 | pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register { | 88 | pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register { |
| 99 | comptime assert(count > 0 and count <= callee_preserved_regs.len); | 89 | comptime assert(count > 0 and count <= callee_preserved_regs.len); |
| 100 | | 90 | |
| ... | @@ -106,24 +96,22 @@ pub fn RegisterManager( | ... | @@ -106,24 +96,22 @@ pub fn RegisterManager( |
| 106 | std.mem.copy(Register, &regs, callee_preserved_regs[0..count]); | 96 | std.mem.copy(Register, &regs, callee_preserved_regs[0..count]); |
| 107 | | 97 | |
| 108 | for (regs) |reg, i| { | 98 | for (regs) |reg, i| { |
| | 99 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null |
| 109 | if (self.isRegFree(reg)) { | 100 | if (self.isRegFree(reg)) { |
| 110 | self.markRegUsed(reg); | 101 | self.markRegUsed(reg); |
| 111 | self.registers.putAssumeCapacityNoClobber(reg, insts[i]); | | |
| 112 | } else { | 102 | } else { |
| 113 | const regs_entry = self.registers.getEntry(reg).?; | 103 | const spilled_inst = self.registers[index].?; |
| 114 | const spilled_inst = regs_entry.value; | | |
| 115 | regs_entry.value = insts[i]; | | |
| 116 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | 104 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 117 | } | 105 | } |
| | 106 | self.registers[index] = insts[i]; |
| 118 | } | 107 | } |
| 119 | | 108 | |
| 120 | break :blk regs; | 109 | break :blk regs; |
| 121 | }; | 110 | }; |
| 122 | } | 111 | } |
| 123 | | 112 | |
| 124 | /// Before calling, must ensureCapacity + 1 on self.registers. | | |
| 125 | pub fn allocReg(self: *Self, inst: *ir.Inst) !Register { | 113 | pub fn allocReg(self: *Self, inst: *ir.Inst) !Register { |
| 126 | return (try allocRegs(self, 1, .{inst}))[0]; | 114 | return (try self.allocRegs(1, .{inst}))[0]; |
| 127 | } | 115 | } |
| 128 | | 116 | |
| 129 | /// Does not track the registers. | 117 | /// Does not track the registers. |
| ... | @@ -150,37 +138,48 @@ pub fn RegisterManager( | ... | @@ -150,37 +138,48 @@ pub fn RegisterManager( |
| 150 | /// Does not track the register. | 138 | /// Does not track the register. |
| 151 | /// Returns `null` if all registers are allocated. | 139 | /// Returns `null` if all registers are allocated. |
| 152 | pub fn tryAllocRegWithoutTracking(self: *Self) ?Register { | 140 | pub fn tryAllocRegWithoutTracking(self: *Self) ?Register { |
| 153 | return if (tryAllocRegsWithoutTracking(self, 1)) |regs| regs[0] else null; | 141 | return if (self.tryAllocRegsWithoutTracking(1)) |regs| regs[0] else null; |
| 154 | } | 142 | } |
| 155 | | 143 | |
| 156 | /// Does not track the register. | 144 | /// Does not track the registers |
| 157 | pub fn allocRegWithoutTracking(self: *Self) !Register { | 145 | pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ![count]Register { |
| 158 | return self.tryAllocRegWithoutTracking() orelse b: { | 146 | return self.tryAllocRegsWithoutTracking(count) orelse blk: { |
| 159 | // We'll take over the first register. Move the instruction that was previously | 147 | // We'll take over the first count registers. Spill |
| 160 | // there to a stack allocation. | 148 | // the instructions that were previously there to a |
| 161 | const reg = callee_preserved_regs[0]; | 149 | // stack allocations. |
| 162 | const regs_entry = self.registers.remove(reg).?; | 150 | var regs: [count]Register = undefined; |
| 163 | const spilled_inst = regs_entry.value; | 151 | std.mem.copy(Register, &regs, callee_preserved_regs[0..count]); |
| 164 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | | |
| 165 | self.markRegFree(reg); | | |
| 166 | | 152 | |
| 167 | break :b reg; | 153 | for (regs) |reg, i| { |
| | 154 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null |
| | 155 | if (!self.isRegFree(reg)) { |
| | 156 | const spilled_inst = self.registers[index].?; |
| | 157 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| | 158 | self.registers[index] = null; |
| | 159 | self.markRegFree(reg); |
| | 160 | } |
| | 161 | } |
| | 162 | |
| | 163 | break :blk regs; |
| 168 | }; | 164 | }; |
| 169 | } | 165 | } |
| 170 | | 166 | |
| | 167 | /// Does not track the register. |
| | 168 | pub fn allocRegWithoutTracking(self: *Self) !Register { |
| | 169 | return (try self.allocRegsWithoutTracking(1))[0]; |
| | 170 | } |
| | 171 | |
| 171 | /// Allocates the specified register with the specified | 172 | /// Allocates the specified register with the specified |
| 172 | /// instruction. Spills the register if it is currently | 173 | /// instruction. Spills the register if it is currently |
| 173 | /// allocated. | 174 | /// allocated. |
| 174 | /// Before calling, must ensureCapacity + 1 on self.registers. | | |
| 175 | pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void { | 175 | pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void { |
| 176 | if (!isTracked(reg)) return; | 176 | const index = reg.allocIndex() orelse return; |
| 177 | | 177 | |
| 178 | if (!self.isRegFree(reg)) { | 178 | if (!self.isRegFree(reg)) { |
| 179 | // Move the instruction that was previously there to a | 179 | // Move the instruction that was previously there to a |
| 180 | // stack allocation. | 180 | // stack allocation. |
| 181 | const regs_entry = self.registers.getEntry(reg).?; | 181 | const spilled_inst = self.registers[index].?; |
| 182 | const spilled_inst = regs_entry.value; | 182 | self.registers[index] = inst; |
| 183 | regs_entry.value = inst; | | |
| 184 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | 183 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 185 | } else { | 184 | } else { |
| 186 | self.getRegAssumeFree(reg, inst); | 185 | self.getRegAssumeFree(reg, inst); |
| ... | @@ -190,34 +189,33 @@ pub fn RegisterManager( | ... | @@ -190,34 +189,33 @@ pub fn RegisterManager( |
| 190 | /// Spills the register if it is currently allocated. | 189 | /// Spills the register if it is currently allocated. |
| 191 | /// Does not track the register. | 190 | /// Does not track the register. |
| 192 | pub fn getRegWithoutTracking(self: *Self, reg: Register) !void { | 191 | pub fn getRegWithoutTracking(self: *Self, reg: Register) !void { |
| 193 | if (!isTracked(reg)) return; | 192 | const index = reg.allocIndex() orelse return; |
| 194 | | 193 | |
| 195 | if (!self.isRegFree(reg)) { | 194 | if (!self.isRegFree(reg)) { |
| 196 | // Move the instruction that was previously there to a | 195 | // Move the instruction that was previously there to a |
| 197 | // stack allocation. | 196 | // stack allocation. |
| 198 | const regs_entry = self.registers.remove(reg).?; | 197 | const spilled_inst = self.registers[index].?; |
| 199 | const spilled_inst = regs_entry.value; | | |
| 200 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | 198 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 201 | self.markRegFree(reg); | 199 | self.markRegFree(reg); |
| 202 | } | 200 | } |
| 203 | } | 201 | } |
| 204 | | 202 | |
| 205 | /// Allocates the specified register with the specified | 203 | /// Allocates the specified register with the specified |
| 206 | /// instruction. Assumes that the register is free and no | 204 | /// instruction. Asserts that the register is free and no |
| 207 | /// spilling is necessary. | 205 | /// spilling is necessary. |
| 208 | /// Before calling, must ensureCapacity + 1 on self.registers. | | |
| 209 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void { | 206 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void { |
| 210 | if (!isTracked(reg)) return; | 207 | const index = reg.allocIndex() orelse return; |
| 211 | | 208 | |
| 212 | self.registers.putAssumeCapacityNoClobber(reg, inst); | 209 | assert(self.registers[index] == null); |
| | 210 | self.registers[index] = inst; |
| 213 | self.markRegUsed(reg); | 211 | self.markRegUsed(reg); |
| 214 | } | 212 | } |
| 215 | | 213 | |
| 216 | /// Marks the specified register as free | 214 | /// Marks the specified register as free |
| 217 | pub fn freeReg(self: *Self, reg: Register) void { | 215 | pub fn freeReg(self: *Self, reg: Register) void { |
| 218 | if (!isTracked(reg)) return; | 216 | const index = reg.allocIndex() orelse return; |
| 219 | | 217 | |
| 220 | _ = self.registers.remove(reg); | 218 | self.registers[index] = null; |
| 221 | self.markRegFree(reg); | 219 | self.markRegFree(reg); |
| 222 | } | 220 | } |
| 223 | }; | 221 | }; |
| ... | @@ -247,7 +245,6 @@ const MockFunction = struct { | ... | @@ -247,7 +245,6 @@ const MockFunction = struct { |
| 247 | const Self = @This(); | 245 | const Self = @This(); |
| 248 | | 246 | |
| 249 | pub fn deinit(self: *Self) void { | 247 | pub fn deinit(self: *Self) void { |
| 250 | self.register_manager.deinit(self.allocator); | | |
| 251 | self.spilled.deinit(self.allocator); | 248 | self.spilled.deinit(self.allocator); |
| 252 | } | 249 | } |
| 253 | | 250 | |
| ... | @@ -273,7 +270,6 @@ test "tryAllocReg: no spilling" { | ... | @@ -273,7 +270,6 @@ test "tryAllocReg: no spilling" { |
| 273 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 270 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 274 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); | 271 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); |
| 275 | | 272 | |
| 276 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | | |
| 277 | std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction)); | 273 | std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction)); |
| 278 | std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction)); | 274 | std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction)); |
| 279 | std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction)); | 275 | std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction)); |
| ... | @@ -305,7 +301,6 @@ test "allocReg: spilling" { | ... | @@ -305,7 +301,6 @@ test "allocReg: spilling" { |
| 305 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 301 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 306 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); | 302 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); |
| 307 | | 303 | |
| 308 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | | |
| 309 | std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction)); | 304 | std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction)); |
| 310 | std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction)); | 305 | std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction)); |
| 311 | | 306 | |
| ... | @@ -336,14 +331,12 @@ test "getReg" { | ... | @@ -336,14 +331,12 @@ test "getReg" { |
| 336 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 331 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 337 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); | 332 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); |
| 338 | | 333 | |
| 339 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | | |
| 340 | try function.register_manager.getReg(.r3, &mock_instruction); | 334 | try function.register_manager.getReg(.r3, &mock_instruction); |
| 341 | | 335 | |
| 342 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 336 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 343 | std.testing.expect(function.register_manager.isRegAllocated(.r3)); | 337 | std.testing.expect(function.register_manager.isRegAllocated(.r3)); |
| 344 | | 338 | |
| 345 | // Spill r3 | 339 | // Spill r3 |
| 346 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | | |
| 347 | try function.register_manager.getReg(.r3, &mock_instruction); | 340 | try function.register_manager.getReg(.r3, &mock_instruction); |
| 348 | | 341 | |
| 349 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 342 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |