authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-05-14 17:09:11+08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-14 15:12:33-04:00
log8b4e91e18ce425902bef726ec3731d266867a6f7
tree1279ffd90a129d97d37ac9e9357d550fd57526ec
parent5185b5619ac703755849da70d158b5a45e5673a7

stage2 register manager: clean up API and add more unit tests


2 files changed, 228 insertions(+), 165 deletions(-)

src/codegen.zig+9-9
......@@ -952,7 +952,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
952952 /// allocated. A second call to `copyToTmpRegister` may return the same register.
953953 /// This can have a side effect of spilling instructions to the stack to free up a register.
954954 fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register {
955 const reg = try self.register_manager.allocRegWithoutTracking(&.{});
955 const reg = try self.register_manager.allocReg(null, &.{});
956956 try self.genSetReg(src, ty, reg, mcv);
957957 return reg;
958958 }
......@@ -2228,7 +2228,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22282228 switch (mc_arg) {
22292229 .none => continue,
22302230 .register => |reg| {
2231 try self.register_manager.getRegWithoutTracking(reg);
2231 try self.register_manager.getReg(reg, null);
22322232 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
22332233 },
22342234 .stack_offset => {
......@@ -2370,7 +2370,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23702370 .compare_flags_signed => unreachable,
23712371 .compare_flags_unsigned => unreachable,
23722372 .register => |reg| {
2373 try self.register_manager.getRegWithoutTracking(reg);
2373 try self.register_manager.getReg(reg, null);
23742374 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
23752375 },
23762376 .stack_offset => {
......@@ -2433,7 +2433,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24332433 .compare_flags_signed => unreachable,
24342434 .compare_flags_unsigned => unreachable,
24352435 .register => |reg| {
2436 try self.register_manager.getRegWithoutTracking(reg);
2436 try self.register_manager.getReg(reg, null);
24372437 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
24382438 },
24392439 .stack_offset => {
......@@ -2486,7 +2486,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24862486 .register => |reg| {
24872487 // TODO prevent this macho if block to be generated for all archs
24882488 switch (arch) {
2489 .x86_64, .aarch64 => try self.register_manager.getRegWithoutTracking(reg),
2489 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),
24902490 else => unreachable,
24912491 }
24922492 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
......@@ -3190,7 +3190,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31903190
31913191 const arg = inst.args[i];
31923192 const arg_mcv = try self.resolveInst(arg);
3193 try self.register_manager.getRegWithoutTracking(reg);
3193 try self.register_manager.getReg(reg, null);
31943194 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
31953195 }
31963196
......@@ -3223,7 +3223,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32233223
32243224 const arg = inst.args[i];
32253225 const arg_mcv = try self.resolveInst(arg);
3226 try self.register_manager.getRegWithoutTracking(reg);
3226 try self.register_manager.getReg(reg, null);
32273227 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
32283228 }
32293229
......@@ -3258,7 +3258,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32583258
32593259 const arg = inst.args[i];
32603260 const arg_mcv = try self.resolveInst(arg);
3261 try self.register_manager.getRegWithoutTracking(reg);
3261 try self.register_manager.getReg(reg, null);
32623262 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
32633263 }
32643264
......@@ -3291,7 +3291,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32913291
32923292 const arg = inst.args[i];
32933293 const arg_mcv = try self.resolveInst(arg);
3294 try self.register_manager.getRegWithoutTracking(reg);
3294 try self.register_manager.getReg(reg, null);
32953295 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
32963296 }
32973297
src/register_manager.zig+219-156
......@@ -7,6 +7,9 @@ const ir = @import("ir.zig");
77const Type = @import("type.zig").Type;
88const Module = @import("Module.zig");
99const LazySrcLoc = Module.LazySrcLoc;
10const expect = std.testing.expect;
11const expectEqual = std.testing.expectEqual;
12const expectEqualSlices = std.testing.expectEqualSlices;
1013
1114const log = std.log.scoped(.register_manager);
1215
......@@ -66,77 +69,14 @@ pub fn RegisterManager(
6669 return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;
6770 }
6871
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.
7075 pub fn tryAllocRegs(
7176 self: *Self,
7277 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,
14080 ) ?[count]Register {
14181 comptime if (callee_preserved_regs.len == 0) return null;
14282 comptime assert(count > 0 and count <= callee_preserved_regs.len);
......@@ -156,18 +96,40 @@ pub fn RegisterManager(
15696 }
15797 }
15898
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;
160111 }
161112
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;
166118 }
167119
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: {
171133 // We'll take over the first count registers. Spill
172134 // the instructions that were previously there to a
173135 // stack allocations.
......@@ -179,11 +141,22 @@ pub fn RegisterManager(
179141 regs[i] = reg;
180142
181143 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 }
187160 }
188161
189162 i += 1;
......@@ -193,39 +166,36 @@ pub fn RegisterManager(
193166 };
194167 }
195168
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];
216173 }
217174
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 {
221179 const index = reg.allocIndex() orelse return;
222180
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 }
229199 }
230200 }
231201
......@@ -250,42 +220,63 @@ pub fn RegisterManager(
250220 };
251221}
252222
253const MockRegister = enum(u2) {
223const MockRegister1 = enum(u2) {
254224 r0,
255225 r1,
256226 r2,
257227 r3,
258228
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| {
261231 if (self == cpreg) return i;
262232 }
263233 return null;
264234 }
265};
266
267const mock_callee_preserved_regs = [_]MockRegister{ .r2, .r3 };
268235
269const 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};
273238
274 const Self = @This();
239const MockRegister2 = enum(u2) {
240 r0,
241 r1,
242 r2,
243 r3,
275244
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;
278250 }
279251
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 };
283253};
284254
285test "tryAllocReg: no spilling" {
255fn 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
273const MockFunction1 = MockFunction(MockRegister1);
274const MockFunction2 = MockFunction(MockRegister2);
275
276test "default state" {
286277 const allocator = std.testing.allocator;
287278
288 var function = MockFunction{
279 var function = MockFunction1{
289280 .allocator = allocator,
290281 };
291282 defer function.deinit();
......@@ -296,27 +287,48 @@ test "tryAllocReg: no spilling" {
296287 .src = .unneeded,
297288 };
298289
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
296test "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 };
301309
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, &.{}));
305313
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));
308318
309319 function.register_manager.freeReg(.r2);
310320 function.register_manager.freeReg(.r3);
311321
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));
314326}
315327
316328test "allocReg: spilling" {
317329 const allocator = std.testing.allocator;
318330
319 var function = MockFunction{
331 var function = MockFunction1{
320332 .allocator = allocator,
321333 };
322334 defer function.deinit();
......@@ -327,26 +339,28 @@ test "allocReg: spilling" {
327339 .src = .unneeded,
328340 };
329341
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, &.{}));
335344
336345 // 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);
339348
340349 // No spilling necessary
341350 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}));
344358}
345359
346test "getReg" {
360test "tryAllocRegs" {
347361 const allocator = std.testing.allocator;
348362
349 var function = MockFunction{
363 var function = MockFunction2{
350364 .allocator = allocator,
351365 };
352366 defer function.deinit();
......@@ -357,18 +371,67 @@ test "getReg" {
357371 .src = .unneeded,
358372 };
359373
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
383test "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
408test "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 };
362421
363422 try function.register_manager.getReg(.r3, &mock_instruction);
364423
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));
367428
368429 // Spill r3
369430 try function.register_manager.getReg(.r3, &mock_instruction);
370431
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);
374437}