| ... | ... | @@ -7,6 +7,9 @@ const ir = @import("ir.zig"); |
| 7 | 7 | const Type = @import("type.zig").Type; |
| 8 | 8 | const Module = @import("Module.zig"); |
| 9 | 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 | 14 | const log = std.log.scoped(.register_manager); |
| 12 | 15 | |
| ... | ... | @@ -66,77 +69,14 @@ pub fn RegisterManager( |
| 66 | 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 | 75 | pub fn tryAllocRegs( |
| 71 | 76 | self: *Self, |
| 72 | 77 | comptime count: comptime_int, |
| 73 | | insts: [count]*ir.Inst, |
| 74 | | exceptions: []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, |
| 78 | insts: [count]?*ir.Inst, |
| 79 | exceptions: []const Register, |
| 140 | 80 | ) ?[count]Register { |
| 141 | 81 | comptime if (callee_preserved_regs.len == 0) return null; |
| 142 | 82 | comptime assert(count > 0 and count <= callee_preserved_regs.len); |
| ... | ... | @@ -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. |
| 163 | | /// Returns `null` if all registers are allocated. |
| 164 | | pub fn tryAllocRegWithoutTracking(self: *Self, exceptions: []Register) ?Register { |
| 165 | | return if (self.tryAllocRegsWithoutTracking(1, exceptions)) |regs| regs[0] else null; |
| 113 | /// Allocates a register and optionally tracks it with a |
| 114 | /// corresponding instruction. Returns `null` if all registers |
| 115 | /// are allocated. |
| 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 |
| 169 | | pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int, exceptions: []Register) ![count]Register { |
| 170 | | return self.tryAllocRegsWithoutTracking(count, exceptions) orelse blk: { |
| 120 | /// Allocates a specified number of registers, optionally |
| 121 | /// tracking them. Asserts that count + exceptions.len is not |
| 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 | 133 | // We'll take over the first count registers. Spill |
| 172 | 134 | // the instructions that were previously there to a |
| 173 | 135 | // stack allocations. |
| ... | ... | @@ -179,11 +141,22 @@ pub fn RegisterManager( |
| 179 | 141 | regs[i] = reg; |
| 180 | 142 | |
| 181 | 143 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null |
| 182 | | if (!self.isRegFree(reg)) { |
| 183 | | const spilled_inst = self.registers[index].?; |
| 184 | | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 185 | | self.registers[index] = null; |
| 186 | | self.markRegFree(reg); |
| 144 | if (insts[i]) |inst| { |
| 145 | // Track the register |
| 146 | if (self.isRegFree(reg)) { |
| 147 | self.markRegUsed(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 | 162 | i += 1; |
| ... | ... | @@ -193,39 +166,36 @@ pub fn RegisterManager( |
| 193 | 166 | }; |
| 194 | 167 | } |
| 195 | 168 | |
| 196 | | /// Does not track the register. |
| 197 | | pub fn allocRegWithoutTracking(self: *Self, exceptions: []Register) !Register { |
| 198 | | return (try self.allocRegsWithoutTracking(1, exceptions))[0]; |
| 199 | | } |
| 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 | | } |
| 169 | /// Allocates a register and optionally tracks it with a |
| 170 | /// corresponding instruction. |
| 171 | pub fn allocReg(self: *Self, inst: ?*ir.Inst, exceptions: []const Register) !Register { |
| 172 | return (try self.allocRegs(1, .{inst}, exceptions))[0]; |
| 216 | 173 | } |
| 217 | 174 | |
| 218 | | /// Spills the register if it is currently allocated. |
| 219 | | /// Does not track the register. |
| 220 | | pub fn getRegWithoutTracking(self: *Self, reg: Register) !void { |
| 175 | /// Spills the register if it is currently allocated. If a |
| 176 | /// corresponding instruction is passed, will also track this |
| 177 | /// register. |
| 178 | pub fn getReg(self: *Self, reg: Register, inst: ?*ir.Inst) !void { |
| 221 | 179 | const index = reg.allocIndex() orelse return; |
| 222 | 180 | |
| 223 | | if (!self.isRegFree(reg)) { |
| 224 | | // Move the instruction that was previously there to a |
| 225 | | // stack allocation. |
| 226 | | const spilled_inst = self.registers[index].?; |
| 227 | | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 228 | | self.markRegFree(reg); |
| 181 | if (inst) |tracked_inst| |
| 182 | if (!self.isRegFree(reg)) { |
| 183 | // Move the instruction that was previously there to a |
| 184 | // stack allocation. |
| 185 | const spilled_inst = self.registers[index].?; |
| 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 | 220 | }; |
| 251 | 221 | } |
| 252 | 222 | |
| 253 | | const MockRegister = enum(u2) { |
| 223 | const MockRegister1 = enum(u2) { |
| 254 | 224 | r0, |
| 255 | 225 | r1, |
| 256 | 226 | r2, |
| 257 | 227 | r3, |
| 258 | 228 | |
| 259 | | pub fn allocIndex(self: MockRegister) ?u2 { |
| 260 | | inline for (mock_callee_preserved_regs) |cpreg, i| { |
| 229 | pub fn allocIndex(self: MockRegister1) ?u2 { |
| 230 | inline for (callee_preserved_regs) |cpreg, i| { |
| 261 | 231 | if (self == cpreg) return i; |
| 262 | 232 | } |
| 263 | 233 | return null; |
| 264 | 234 | } |
| 265 | | }; |
| 266 | | |
| 267 | | const mock_callee_preserved_regs = [_]MockRegister{ .r2, .r3 }; |
| 268 | 235 | |
| 269 | | const MockFunction = struct { |
| 270 | | allocator: *Allocator, |
| 271 | | register_manager: RegisterManager(Self, MockRegister, &mock_callee_preserved_regs) = .{}, |
| 272 | | spilled: std.ArrayListUnmanaged(MockRegister) = .{}, |
| 236 | const callee_preserved_regs = [_]MockRegister1{ .r2, .r3 }; |
| 237 | }; |
| 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 { |
| 277 | | self.spilled.deinit(self.allocator); |
| 245 | pub fn allocIndex(self: MockRegister2) ?u2 { |
| 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 { |
| 281 | | try self.spilled.append(self.allocator, reg); |
| 282 | | } |
| 252 | const callee_preserved_regs = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; |
| 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 | 277 | const allocator = std.testing.allocator; |
| 287 | 278 | |
| 288 | | var function = MockFunction{ |
| 279 | var function = MockFunction1{ |
| 289 | 280 | .allocator = allocator, |
| 290 | 281 | }; |
| 291 | 282 | defer function.deinit(); |
| ... | ... | @@ -296,27 +287,48 @@ test "tryAllocReg: no spilling" { |
| 296 | 287 | .src = .unneeded, |
| 297 | 288 | }; |
| 298 | 289 | |
| 299 | | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 300 | | try std.testing.expect(!function.register_manager.isRegAllocated(.r3)); |
| 290 | try expect(!function.register_manager.isRegAllocated(.r2)); |
| 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, &.{})); |
| 303 | | try std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction, &.{})); |
| 304 | | try std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction, &.{})); |
| 310 | try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(&mock_instruction, &.{})); |
| 311 | try expectEqual(@as(?MockRegister1, .r3), 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)); |
| 307 | | try std.testing.expect(function.register_manager.isRegAllocated(.r3)); |
| 314 | try expect(function.register_manager.isRegAllocated(.r2)); |
| 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 | 319 | function.register_manager.freeReg(.r2); |
| 310 | 320 | function.register_manager.freeReg(.r3); |
| 311 | 321 | |
| 312 | | try std.testing.expect(function.register_manager.isRegAllocated(.r2)); |
| 313 | | try std.testing.expect(function.register_manager.isRegAllocated(.r3)); |
| 322 | try expect(function.register_manager.isRegAllocated(.r2)); |
| 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 | 328 | test "allocReg: spilling" { |
| 317 | 329 | const allocator = std.testing.allocator; |
| 318 | 330 | |
| 319 | | var function = MockFunction{ |
| 331 | var function = MockFunction1{ |
| 320 | 332 | .allocator = allocator, |
| 321 | 333 | }; |
| 322 | 334 | defer function.deinit(); |
| ... | ... | @@ -327,26 +339,28 @@ test "allocReg: spilling" { |
| 327 | 339 | .src = .unneeded, |
| 328 | 340 | }; |
| 329 | 341 | |
| 330 | | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 331 | | try std.testing.expect(!function.register_manager.isRegAllocated(.r3)); |
| 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, &.{})); |
| 342 | try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 343 | try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 335 | 344 | |
| 336 | 345 | // Spill a register |
| 337 | | try std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 338 | | try std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); |
| 346 | try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 347 | try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items); |
| 339 | 348 | |
| 340 | 349 | // No spilling necessary |
| 341 | 350 | function.register_manager.freeReg(.r3); |
| 342 | | try std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 343 | | try std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); |
| 351 | try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(&mock_instruction, &.{})); |
| 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 | 361 | const allocator = std.testing.allocator; |
| 348 | 362 | |
| 349 | | var function = MockFunction{ |
| 363 | var function = MockFunction2{ |
| 350 | 364 | .allocator = allocator, |
| 351 | 365 | }; |
| 352 | 366 | defer function.deinit(); |
| ... | ... | @@ -357,18 +371,67 @@ test "getReg" { |
| 357 | 371 | .src = .unneeded, |
| 358 | 372 | }; |
| 359 | 373 | |
| 360 | | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 361 | | try std.testing.expect(!function.register_manager.isRegAllocated(.r3)); |
| 374 | try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?); |
| 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 | 422 | try function.register_manager.getReg(.r3, &mock_instruction); |
| 364 | 423 | |
| 365 | | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 366 | | try std.testing.expect(function.register_manager.isRegAllocated(.r3)); |
| 424 | try expect(!function.register_manager.isRegAllocated(.r2)); |
| 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 | 429 | // Spill r3 |
| 369 | 430 | try function.register_manager.getReg(.r3, &mock_instruction); |
| 370 | 431 | |
| 371 | | try std.testing.expect(!function.register_manager.isRegAllocated(.r2)); |
| 372 | | try std.testing.expect(function.register_manager.isRegAllocated(.r3)); |
| 373 | | try std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r3}, function.spilled.items); |
| 432 | try expect(!function.register_manager.isRegAllocated(.r2)); |
| 433 | try expect(function.register_manager.isRegAllocated(.r3)); |
| 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 | } |