| ... | @@ -7,6 +7,9 @@ const ir = @import("ir.zig"); | ... | @@ -7,6 +7,9 @@ const ir = @import("ir.zig"); |
| 7 | const Type = @import("type.zig").Type; | 7 | const Type = @import("type.zig").Type; |
| 8 | const Module = @import("Module.zig"); | 8 | const Module = @import("Module.zig"); |
| 9 | const LazySrcLoc = Module.LazySrcLoc; | 9 | const LazySrcLoc = Module.LazySrcLoc; |
| | 10 | const expect = std.testing.expect; |
| | 11 | const expectEqual = std.testing.expectEqual; |
| | 12 | const expectEqualSlices = std.testing.expectEqualSlices; |
| 10 | | 13 | |
| 11 | const log = std.log.scoped(.register_manager); | 14 | const log = std.log.scoped(.register_manager); |
| 12 | | 15 | |
| ... | @@ -66,77 +69,14 @@ pub fn RegisterManager( | ... | @@ -66,77 +69,14 @@ pub fn RegisterManager( |
| 66 | return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; | 69 | return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; |
| 67 | } | 70 | } |
| 68 | | 71 | |
| 69 | /// Returns `null` if all registers are allocated. | 72 | /// Allocates a specified number of registers, optionally |
| | 73 | /// tracking them. Returns `null` if not enough registers are |
| | 74 | /// free. |
| 70 | pub fn tryAllocRegs( | 75 | pub fn tryAllocRegs( |
| 71 | self: *Self, | 76 | self: *Self, |
| 72 | comptime count: comptime_int, | 77 | comptime count: comptime_int, |
| 73 | insts: [count]*ir.Inst, | 78 | insts: [count]?*ir.Inst, |
| 74 | exceptions: []Register, | 79 | exceptions: []const Register, |
| 75 | ) ?[count]Register { | | |
| 76 | if (self.tryAllocRegsWithoutTracking(count, exceptions)) |regs| { | | |
| 77 | for (regs) |reg, i| { | | |
| 78 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null | | |
| 79 | self.registers[index] = insts[i]; | | |
| 80 | self.markRegUsed(reg); | | |
| 81 | } | | |
| 82 | | | |
| 83 | return regs; | | |
| 84 | } else { | | |
| 85 | return null; | | |
| 86 | } | | |
| 87 | } | | |
| 88 | | | |
| 89 | /// Returns `null` if all registers are allocated. | | |
| 90 | pub fn tryAllocReg(self: *Self, inst: *ir.Inst, exceptions: []Register) ?Register { | | |
| 91 | return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null; | | |
| 92 | } | | |
| 93 | | | |
| 94 | pub fn allocRegs( | | |
| 95 | self: *Self, | | |
| 96 | comptime count: comptime_int, | | |
| 97 | insts: [count]*ir.Inst, | | |
| 98 | exceptions: []Register, | | |
| 99 | ) ![count]Register { | | |
| 100 | comptime assert(count > 0 and count <= callee_preserved_regs.len); | | |
| 101 | assert(count + exceptions.len <= callee_preserved_regs.len); | | |
| 102 | | | |
| 103 | return self.tryAllocRegs(count, insts, exceptions) orelse blk: { | | |
| 104 | // We'll take over the first count registers. Spill | | |
| 105 | // the instructions that were previously there to a | | |
| 106 | // stack allocations. | | |
| 107 | var regs: [count]Register = undefined; | | |
| 108 | var i: usize = 0; | | |
| 109 | for (callee_preserved_regs) |reg| { | | |
| 110 | if (i >= count) break; | | |
| 111 | if (mem.indexOfScalar(Register, exceptions, reg) != null) continue; | | |
| 112 | regs[i] = reg; | | |
| 113 | | | |
| 114 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null | | |
| 115 | if (self.isRegFree(reg)) { | | |
| 116 | self.markRegUsed(reg); | | |
| 117 | } else { | | |
| 118 | const spilled_inst = self.registers[index].?; | | |
| 119 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | | |
| 120 | } | | |
| 121 | self.registers[index] = insts[i]; | | |
| 122 | | | |
| 123 | i += 1; | | |
| 124 | } | | |
| 125 | | | |
| 126 | break :blk regs; | | |
| 127 | }; | | |
| 128 | } | | |
| 129 | | | |
| 130 | pub fn allocReg(self: *Self, inst: *ir.Inst, exceptions: []Register) !Register { | | |
| 131 | return (try self.allocRegs(1, .{inst}, exceptions))[0]; | | |
| 132 | } | | |
| 133 | | | |
| 134 | /// Does not track the registers. | | |
| 135 | /// Returns `null` if not enough registers are free. | | |
| 136 | pub fn tryAllocRegsWithoutTracking( | | |
| 137 | self: *Self, | | |
| 138 | comptime count: comptime_int, | | |
| 139 | exceptions: []Register, | | |
| 140 | ) ?[count]Register { | 80 | ) ?[count]Register { |
| 141 | comptime if (callee_preserved_regs.len == 0) return null; | 81 | comptime if (callee_preserved_regs.len == 0) return null; |
| 142 | comptime assert(count > 0 and count <= callee_preserved_regs.len); | 82 | comptime assert(count > 0 and count <= callee_preserved_regs.len); |
| ... | @@ -156,18 +96,40 @@ pub fn RegisterManager( | ... | @@ -156,18 +96,40 @@ pub fn RegisterManager( |
| 156 | } | 96 | } |
| 157 | } | 97 | } |
| 158 | | 98 | |
| 159 | return if (i < count) null else regs; | 99 | if (i == count) { |
| | 100 | for (regs) |reg, j| { |
| | 101 | if (insts[j]) |inst| { |
| | 102 | // Track the register |
| | 103 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null |
| | 104 | self.registers[index] = inst; |
| | 105 | self.markRegUsed(reg); |
| | 106 | } |
| | 107 | } |
| | 108 | |
| | 109 | return regs; |
| | 110 | } else return null; |
| 160 | } | 111 | } |
| 161 | | 112 | |
| 162 | /// Does not track the register. | 113 | /// Allocates a register and optionally tracks it with a |
| 163 | /// Returns `null` if all registers are allocated. | 114 | /// corresponding instruction. Returns `null` if all registers |
| 164 | pub fn tryAllocRegWithoutTracking(self: *Self, exceptions: []Register) ?Register { | 115 | /// are allocated. |
| 165 | return if (self.tryAllocRegsWithoutTracking(1, exceptions)) |regs| regs[0] else null; | 116 | pub fn tryAllocReg(self: *Self, inst: ?*ir.Inst, exceptions: []const Register) ?Register { |
| | 117 | return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null; |
| 166 | } | 118 | } |
| 167 | | 119 | |
| 168 | /// Does not track the registers | 120 | /// Allocates a specified number of registers, optionally |
| 169 | pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int, exceptions: []Register) ![count]Register { | 121 | /// tracking them. Asserts that count + exceptions.len is not |
| 170 | return self.tryAllocRegsWithoutTracking(count, exceptions) orelse blk: { | 122 | /// larger than the total number of registers available. |
| | 123 | pub fn allocRegs( |
| | 124 | self: *Self, |
| | 125 | comptime count: comptime_int, |
| | 126 | insts: [count]?*ir.Inst, |
| | 127 | exceptions: []const Register, |
| | 128 | ) ![count]Register { |
| | 129 | comptime assert(count > 0 and count <= callee_preserved_regs.len); |
| | 130 | assert(count + exceptions.len <= callee_preserved_regs.len); |
| | 131 | |
| | 132 | return self.tryAllocRegs(count, insts, exceptions) orelse blk: { |
| 171 | // We'll take over the first count registers. Spill | 133 | // We'll take over the first count registers. Spill |
| 172 | // the instructions that were previously there to a | 134 | // the instructions that were previously there to a |
| 173 | // stack allocations. | 135 | // stack allocations. |
| ... | @@ -179,11 +141,22 @@ pub fn RegisterManager( | ... | @@ -179,11 +141,22 @@ pub fn RegisterManager( |
| 179 | regs[i] = reg; | 141 | regs[i] = reg; |
| 180 | | 142 | |
| 181 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null | 143 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null |
| 182 | if (!self.isRegFree(reg)) { | 144 | if (insts[i]) |inst| { |
| 183 | const spilled_inst = self.registers[index].?; | 145 | // Track the register |
| 184 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | 146 | if (self.isRegFree(reg)) { |
| 185 | self.registers[index] = null; | 147 | self.markRegUsed(reg); |
| 186 | self.markRegFree(reg); | 148 | } else { |
| | 149 | const spilled_inst = self.registers[index].?; |
| | 150 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| | 151 | } |
| | 152 | self.registers[index] = inst; |
| | 153 | } else { |
| | 154 | // Don't track the register |
| | 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.freeReg(reg); |
| | 159 | } |
| 187 | } | 160 | } |
| 188 | | 161 | |
| 189 | i += 1; | 162 | i += 1; |
| ... | @@ -193,39 +166,36 @@ pub fn RegisterManager( | ... | @@ -193,39 +166,36 @@ pub fn RegisterManager( |
| 193 | }; | 166 | }; |
| 194 | } | 167 | } |
| 195 | | 168 | |
| 196 | /// Does not track the register. | 169 | /// Allocates a register and optionally tracks it with a |
| 197 | pub fn allocRegWithoutTracking(self: *Self, exceptions: []Register) !Register { | 170 | /// corresponding instruction. |
| 198 | return (try self.allocRegsWithoutTracking(1, exceptions))[0]; | 171 | pub fn allocReg(self: *Self, inst: ?*ir.Inst, exceptions: []const Register) !Register { |
| 199 | } | 172 | return (try self.allocRegs(1, .{inst}, exceptions))[0]; |
| 200 | | | |
| 201 | /// Allocates the specified register with the specified | | |
| 202 | /// instruction. Spills the register if it is currently | | |
| 203 | /// allocated. | | |
| 204 | pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void { | | |
| 205 | const index = reg.allocIndex() orelse return; | | |
| 206 | | | |
| 207 | if (!self.isRegFree(reg)) { | | |
| 208 | // Move the instruction that was previously there to a | | |
| 209 | // stack allocation. | | |
| 210 | const spilled_inst = self.registers[index].?; | | |
| 211 | self.registers[index] = inst; | | |
| 212 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | | |
| 213 | } else { | | |
| 214 | self.getRegAssumeFree(reg, inst); | | |
| 215 | } | | |
| 216 | } | 173 | } |
| 217 | | 174 | |
| 218 | /// Spills the register if it is currently allocated. | 175 | /// Spills the register if it is currently allocated. If a |
| 219 | /// Does not track the register. | 176 | /// corresponding instruction is passed, will also track this |
| 220 | pub fn getRegWithoutTracking(self: *Self, reg: Register) !void { | 177 | /// register. |
| | 178 | pub fn getReg(self: *Self, reg: Register, inst: ?*ir.Inst) !void { |
| 221 | const index = reg.allocIndex() orelse return; | 179 | const index = reg.allocIndex() orelse return; |
| 222 | | 180 | |
| 223 | if (!self.isRegFree(reg)) { | 181 | if (inst) |tracked_inst| |
| 224 | // Move the instruction that was previously there to a | 182 | if (!self.isRegFree(reg)) { |
| 225 | // stack allocation. | 183 | // Move the instruction that was previously there to a |
| 226 | const spilled_inst = self.registers[index].?; | 184 | // stack allocation. |
| 227 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | 185 | const spilled_inst = self.registers[index].?; |
| 228 | self.markRegFree(reg); | 186 | self.registers[index] = tracked_inst; |
| | 187 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| | 188 | } else { |
| | 189 | self.getRegAssumeFree(reg, tracked_inst); |
| | 190 | } |
| | 191 | else { |
| | 192 | if (!self.isRegFree(reg)) { |
| | 193 | // Move the instruction that was previously there to a |
| | 194 | // stack allocation. |
| | 195 | const spilled_inst = self.registers[index].?; |
| | 196 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| | 197 | self.freeReg(reg); |
| | 198 | } |
| 229 | } | 199 | } |
| 230 | } | 200 | } |
| 231 | | 201 | |
| ... | @@ -250,42 +220,63 @@ pub fn RegisterManager( | ... | @@ -250,42 +220,63 @@ pub fn RegisterManager( |
| 250 | }; | 220 | }; |
| 251 | } | 221 | } |
| 252 | | 222 | |
| 253 | const MockRegister = enum(u2) { | 223 | const MockRegister1 = enum(u2) { |
| 254 | r0, | 224 | r0, |
| 255 | r1, | 225 | r1, |
| 256 | r2, | 226 | r2, |
| 257 | r3, | 227 | r3, |
| 258 | | 228 | |
| 259 | pub fn allocIndex(self: MockRegister) ?u2 { | 229 | pub fn allocIndex(self: MockRegister1) ?u2 { |
| 260 | inline for (mock_callee_preserved_regs) |cpreg, i| { | 230 | inline for (callee_preserved_regs) |cpreg, i| { |
| 261 | if (self == cpreg) return i; | 231 | if (self == cpreg) return i; |
| 262 | } | 232 | } |
| 263 | return null; | 233 | return null; |
| 264 | } | 234 | } |
| 265 | }; | | |
| 266 | | | |
| 267 | const mock_callee_preserved_regs = [_]MockRegister{ .r2, .r3 }; | | |
| 268 | | 235 | |
| 269 | const MockFunction = struct { | 236 | const callee_preserved_regs = [_]MockRegister1{ .r2, .r3 }; |
| 270 | allocator: *Allocator, | 237 | }; |
| 271 | register_manager: RegisterManager(Self, MockRegister, &mock_callee_preserved_regs) = .{}, | | |
| 272 | spilled: std.ArrayListUnmanaged(MockRegister) = .{}, | | |
| 273 | | 238 | |
| 274 | const Self = @This(); | 239 | const MockRegister2 = enum(u2) { |
| | 240 | r0, |
| | 241 | r1, |
| | 242 | r2, |
| | 243 | r3, |
| 275 | | 244 | |
| 276 | pub fn deinit(self: *Self) void { | 245 | pub fn allocIndex(self: MockRegister2) ?u2 { |
| 277 | self.spilled.deinit(self.allocator); | 246 | inline for (callee_preserved_regs) |cpreg, i| { |
| | 247 | if (self == cpreg) return i; |
| | 248 | } |
| | 249 | return null; |
| 278 | } | 250 | } |
| 279 | | 251 | |
| 280 | pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void { | 252 | const callee_preserved_regs = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; |
| 281 | try self.spilled.append(self.allocator, reg); | | |
| 282 | } | | |
| 283 | }; | 253 | }; |
| 284 | | 254 | |
| 285 | test "tryAllocReg: no spilling" { | 255 | fn MockFunction(comptime Register: type) type { |
| | 256 | return struct { |
| | 257 | allocator: *Allocator, |
| | 258 | register_manager: RegisterManager(Self, Register, &Register.callee_preserved_regs) = .{}, |
| | 259 | spilled: std.ArrayListUnmanaged(Register) = .{}, |
| | 260 | |
| | 261 | const Self = @This(); |
| | 262 | |
| | 263 | pub fn deinit(self: *Self) void { |
| | 264 | self.spilled.deinit(self.allocator); |
| | 265 | } |
| | 266 | |
| | 267 | pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void { |
| | 268 | try self.spilled.append(self.allocator, reg); |
| | 269 | } |
| | 270 | }; |
| | 271 | } |
| | 272 | |
| | 273 | const MockFunction1 = MockFunction(MockRegister1); |
| | 274 | const MockFunction2 = MockFunction(MockRegister2); |
| | 275 | |
| | 276 | test "default state" { |
| 286 | const allocator = std.testing.allocator; | 277 | const allocator = std.testing.allocator; |
| 287 | | 278 | |
| 288 | var function = MockFunction{ | 279 | var function = MockFunction1{ |
| 289 | .allocator = allocator, | 280 | .allocator = allocator, |
| 290 | }; | 281 | }; |
| 291 | defer function.deinit(); | 282 | defer function.deinit(); |
| ... | @@ -296,27 +287,48 @@ test "tryAllocReg: no spilling" { | ... | @@ -296,27 +287,48 @@ test "tryAllocReg: no spilling" { |
| 296 | .src = .unneeded, | 287 | .src = .unneeded, |
| 297 | }; | 288 | }; |
| 298 | | 289 | |
| 299 | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 290 | try expect(!function.register_manager.isRegAllocated(.r2)); |
| 300 | try std.testing.expect(!function.register_manager.isRegAllocated(.r3)); | 291 | try expect(!function.register_manager.isRegAllocated(.r3)); |
| | 292 | try expect(function.register_manager.isRegFree(.r2)); |
| | 293 | try expect(function.register_manager.isRegFree(.r3)); |
| | 294 | } |
| | 295 | |
| | 296 | test "tryAllocReg: no spilling" { |
| | 297 | const allocator = std.testing.allocator; |
| | 298 | |
| | 299 | var function = MockFunction1{ |
| | 300 | .allocator = allocator, |
| | 301 | }; |
| | 302 | defer function.deinit(); |
| | 303 | |
| | 304 | var mock_instruction = ir.Inst{ |
| | 305 | .tag = .breakpoint, |
| | 306 | .ty = Type.initTag(.void), |
| | 307 | .src = .unneeded, |
| | 308 | }; |
| 301 | | 309 | |
| 302 | try std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction, &.{})); | 310 | try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(&mock_instruction, &.{})); |
| 303 | try std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction, &.{})); | 311 | try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(&mock_instruction, &.{})); |
| 304 | try std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction, &.{})); | 312 | try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(&mock_instruction, &.{})); |
| 305 | | 313 | |
| 306 | try std.testing.expect(function.register_manager.isRegAllocated(.r2)); | 314 | try expect(function.register_manager.isRegAllocated(.r2)); |
| 307 | try std.testing.expect(function.register_manager.isRegAllocated(.r3)); | 315 | try expect(function.register_manager.isRegAllocated(.r3)); |
| | 316 | try expect(!function.register_manager.isRegFree(.r2)); |
| | 317 | try expect(!function.register_manager.isRegFree(.r3)); |
| 308 | | 318 | |
| 309 | function.register_manager.freeReg(.r2); | 319 | function.register_manager.freeReg(.r2); |
| 310 | function.register_manager.freeReg(.r3); | 320 | function.register_manager.freeReg(.r3); |
| 311 | | 321 | |
| 312 | try std.testing.expect(function.register_manager.isRegAllocated(.r2)); | 322 | try expect(function.register_manager.isRegAllocated(.r2)); |
| 313 | try std.testing.expect(function.register_manager.isRegAllocated(.r3)); | 323 | try expect(function.register_manager.isRegAllocated(.r3)); |
| | 324 | try expect(function.register_manager.isRegFree(.r2)); |
| | 325 | try expect(function.register_manager.isRegFree(.r3)); |
| 314 | } | 326 | } |
| 315 | | 327 | |
| 316 | test "allocReg: spilling" { | 328 | test "allocReg: spilling" { |
| 317 | const allocator = std.testing.allocator; | 329 | const allocator = std.testing.allocator; |
| 318 | | 330 | |
| 319 | var function = MockFunction{ | 331 | var function = MockFunction1{ |
| 320 | .allocator = allocator, | 332 | .allocator = allocator, |
| 321 | }; | 333 | }; |
| 322 | defer function.deinit(); | 334 | defer function.deinit(); |
| ... | @@ -327,26 +339,28 @@ test "allocReg: spilling" { | ... | @@ -327,26 +339,28 @@ test "allocReg: spilling" { |
| 327 | .src = .unneeded, | 339 | .src = .unneeded, |
| 328 | }; | 340 | }; |
| 329 | | 341 | |
| 330 | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 342 | try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 331 | try std.testing.expect(!function.register_manager.isRegAllocated(.r3)); | 343 | try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 332 | | | |
| 333 | try std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); | | |
| 334 | try std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); | | |
| 335 | | 344 | |
| 336 | // Spill a register | 345 | // Spill a register |
| 337 | try std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); | 346 | try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 338 | try std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); | 347 | try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); |
| 339 | | 348 | |
| 340 | // No spilling necessary | 349 | // No spilling necessary |
| 341 | function.register_manager.freeReg(.r3); | 350 | function.register_manager.freeReg(.r3); |
| 342 | try std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); | 351 | try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 343 | try std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); | 352 | try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); |
| | 353 | |
| | 354 | // Exceptions |
| | 355 | function.register_manager.freeReg(.r2); |
| | 356 | function.register_manager.freeReg(.r3); |
| | 357 | try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{.r2})); |
| 344 | } | 358 | } |
| 345 | | 359 | |
| 346 | test "getReg" { | 360 | test "tryAllocRegs" { |
| 347 | const allocator = std.testing.allocator; | 361 | const allocator = std.testing.allocator; |
| 348 | | 362 | |
| 349 | var function = MockFunction{ | 363 | var function = MockFunction2{ |
| 350 | .allocator = allocator, | 364 | .allocator = allocator, |
| 351 | }; | 365 | }; |
| 352 | defer function.deinit(); | 366 | defer function.deinit(); |
| ... | @@ -357,18 +371,67 @@ test "getReg" { | ... | @@ -357,18 +371,67 @@ test "getReg" { |
| 357 | .src = .unneeded, | 371 | .src = .unneeded, |
| 358 | }; | 372 | }; |
| 359 | | 373 | |
| 360 | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 374 | try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?); |
| 361 | try std.testing.expect(!function.register_manager.isRegAllocated(.r3)); | 375 | |
| | 376 | // Exceptions |
| | 377 | function.register_manager.freeReg(.r0); |
| | 378 | function.register_manager.freeReg(.r1); |
| | 379 | function.register_manager.freeReg(.r2); |
| | 380 | try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{.r1}).?); |
| | 381 | } |
| | 382 | |
| | 383 | test "allocRegs" { |
| | 384 | const allocator = std.testing.allocator; |
| | 385 | |
| | 386 | var function = MockFunction2{ |
| | 387 | .allocator = allocator, |
| | 388 | }; |
| | 389 | defer function.deinit(); |
| | 390 | |
| | 391 | var mock_instruction = ir.Inst{ |
| | 392 | .tag = .breakpoint, |
| | 393 | .ty = Type.initTag(.void), |
| | 394 | .src = .unneeded, |
| | 395 | }; |
| | 396 | |
| | 397 | try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, try function.register_manager.allocRegs(3, .{ |
| | 398 | &mock_instruction, |
| | 399 | &mock_instruction, |
| | 400 | &mock_instruction, |
| | 401 | }, &.{})); |
| | 402 | |
| | 403 | // Exceptions |
| | 404 | try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }, &.{.r1})); |
| | 405 | try expectEqualSlices(MockRegister2, &[_]MockRegister2{ .r0, .r2 }, function.spilled.items); |
| | 406 | } |
| | 407 | |
| | 408 | test "getReg" { |
| | 409 | const allocator = std.testing.allocator; |
| | 410 | |
| | 411 | var function = MockFunction1{ |
| | 412 | .allocator = allocator, |
| | 413 | }; |
| | 414 | defer function.deinit(); |
| | 415 | |
| | 416 | var mock_instruction = ir.Inst{ |
| | 417 | .tag = .breakpoint, |
| | 418 | .ty = Type.initTag(.void), |
| | 419 | .src = .unneeded, |
| | 420 | }; |
| 362 | | 421 | |
| 363 | try function.register_manager.getReg(.r3, &mock_instruction); | 422 | try function.register_manager.getReg(.r3, &mock_instruction); |
| 364 | | 423 | |
| 365 | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 424 | try expect(!function.register_manager.isRegAllocated(.r2)); |
| 366 | try std.testing.expect(function.register_manager.isRegAllocated(.r3)); | 425 | try expect(function.register_manager.isRegAllocated(.r3)); |
| | 426 | try expect(function.register_manager.isRegFree(.r2)); |
| | 427 | try expect(!function.register_manager.isRegFree(.r3)); |
| 367 | | 428 | |
| 368 | // Spill r3 | 429 | // Spill r3 |
| 369 | try function.register_manager.getReg(.r3, &mock_instruction); | 430 | try function.register_manager.getReg(.r3, &mock_instruction); |
| 370 | | 431 | |
| 371 | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | 432 | try expect(!function.register_manager.isRegAllocated(.r2)); |
| 372 | try std.testing.expect(function.register_manager.isRegAllocated(.r3)); | 433 | try expect(function.register_manager.isRegAllocated(.r3)); |
| 373 | try std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r3}, function.spilled.items); | 434 | try expect(function.register_manager.isRegFree(.r2)); |
| | 435 | try expect(!function.register_manager.isRegFree(.r3)); |
| | 436 | try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r3}, function.spilled.items); |
| 374 | } | 437 | } |