authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-04-18 20:31:14+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-25 13:20:53-04:00
log3a55cda14d38a2f83e3c6a9ecb782fcb81d1347d
tree7a6654cb77299600cf6039f5a306252c6a3d89fb
parent37b05742ff1544bccf7c8ae9b12c6707a5a54df2

stage2 register manager: Use an array instead of a hashmap for tracking

allocated registers

2 files changed, 72 insertions(+), 67 deletions(-)

src/codegen.zig+29-17
...@@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
449 .rbrace_src = src_data.rbrace_src,449 .rbrace_src = src_data.rbrace_src,
450 .source = src_data.source,450 .source = src_data.source,
451 };451 };
452 defer function.register_manager.deinit(bin_file.allocator);
453 defer function.stack.deinit(bin_file.allocator);452 defer function.stack.deinit(bin_file.allocator);
454 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);453 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
455454
...@@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
779 branch.inst_table.putAssumeCapacity(inst, .dead);778 branch.inst_table.putAssumeCapacity(inst, .dead);
780 switch (prev_value) {779 switch (prev_value) {
781 .register => |reg| {780 .register => |reg| {
782 const canon_reg = toCanonicalReg(reg);781 // TODO separate architectures with registers from
783 self.register_manager.freeReg(canon_reg);782 // stack-based architectures (spu_2)
783 if (callee_preserved_regs.len > 0) {
784 const canon_reg = toCanonicalReg(reg);
785 self.register_manager.freeReg(canon_reg);
786 }
784 },787 },
785 else => {}, // TODO process stack allocation death788 else => {}, // TODO process stack allocation death
786 }789 }
...@@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
920 const ptr_bits = arch.ptrBitWidth();923 const ptr_bits = arch.ptrBitWidth();
921 const ptr_bytes: u64 = @divExact(ptr_bits, 8);924 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
922 if (abi_size <= ptr_bytes) {925 if (abi_size <= ptr_bytes) {
923 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);926 // TODO separate architectures with registers from
924 if (self.register_manager.tryAllocReg(inst)) |reg| {927 // stack-based architectures (spu_2)
925 return MCValue{ .register = registerAlias(reg, abi_size) };928 if (callee_preserved_regs.len > 0) {
929 if (self.register_manager.tryAllocReg(inst)) |reg| {
930 return MCValue{ .register = registerAlias(reg, abi_size) };
931 }
926 }932 }
927 }933 }
928 }934 }
...@@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
952 /// `reg_owner` is the instruction that gets associated with the register in the register table.958 /// `reg_owner` is the instruction that gets associated with the register in the register table.
953 /// This can have a side effect of spilling instructions to the stack to free up a register.959 /// This can have a side effect of spilling instructions to the stack to free up a register.
954 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {960 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
955 try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1));
956
957 const reg = try self.register_manager.allocReg(reg_owner);961 const reg = try self.register_manager.allocReg(reg_owner);
958 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);962 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
959 return MCValue{ .register = reg };963 return MCValue{ .register = reg };
...@@ -1240,10 +1244,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1240,10 +1244,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1240 .register => |reg| {1244 .register => |reg| {
1241 // If it's in the registers table, need to associate the register with the1245 // If it's in the registers table, need to associate the register with the
1242 // new instruction.1246 // new instruction.
1243 if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| {1247 // TODO separate architectures with registers from
1244 entry.value = inst;1248 // stack-based architectures (spu_2)
1249 if (callee_preserved_regs.len > 0) {
1250 if (reg.allocIndex()) |index| {
1251 if (!self.register_manager.isRegFree(reg)) {
1252 self.register_manager.registers[index] = inst;
1253 }
1254 }
1255 log.debug("reusing {} => {*}", .{ reg, inst });
1245 }1256 }
1246 log.debug("reusing {} => {*}", .{ reg, inst });
1247 },1257 },
1248 .stack_offset => |off| {1258 .stack_offset => |off| {
1249 log.debug("reusing stack offset {} => {*}", .{ off, inst });1259 log.debug("reusing stack offset {} => {*}", .{ off, inst });
...@@ -1738,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1738,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1738 const arg_index = self.arg_index;1748 const arg_index = self.arg_index;
1739 self.arg_index += 1;1749 self.arg_index += 1;
17401750
1751 // TODO separate architectures with registers from
1752 // stack-based architectures (spu_2)
1741 if (callee_preserved_regs.len == 0) {1753 if (callee_preserved_regs.len == 0) {
1742 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});1754 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
1743 }1755 }
...@@ -1769,7 +1781,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1769,7 +1781,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17691781
1770 switch (mcv) {1782 switch (mcv) {
1771 .register => |reg| {1783 .register => |reg| {
1772 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
1773 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);1784 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
1774 },1785 },
1775 else => {},1786 else => {},
...@@ -2075,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2075,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2075 switch (mc_arg) {2086 switch (mc_arg) {
2076 .none => continue,2087 .none => continue,
2077 .register => |reg| {2088 .register => |reg| {
2078 try self.register_manager.getRegWithoutTracking(reg);2089 // TODO prevent this macho if block to be generated for all archs
2090 switch (arch) {
2091 .x86_64, .aarch64 => try self.register_manager.getRegWithoutTracking(reg),
2092 else => unreachable,
2093 }
2079 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2094 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2080 },2095 },
2081 .stack_offset => {2096 .stack_offset => {
...@@ -2397,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2397,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2397 const parent_free_registers = self.register_manager.free_registers;2412 const parent_free_registers = self.register_manager.free_registers;
2398 var parent_stack = try self.stack.clone(self.gpa);2413 var parent_stack = try self.stack.clone(self.gpa);
2399 defer parent_stack.deinit(self.gpa);2414 defer parent_stack.deinit(self.gpa);
2400 var parent_registers = try self.register_manager.registers.clone(self.gpa);2415 const parent_registers = self.register_manager.registers;
2401 defer parent_registers.deinit(self.gpa);
24022416
2403 try self.branch_stack.append(.{});2417 try self.branch_stack.append(.{});
24042418
...@@ -2414,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2414,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2414 var saved_then_branch = self.branch_stack.pop();2428 var saved_then_branch = self.branch_stack.pop();
2415 defer saved_then_branch.deinit(self.gpa);2429 defer saved_then_branch.deinit(self.gpa);
24162430
2417 self.register_manager.registers.deinit(self.gpa);
2418 self.register_manager.registers = parent_registers;2431 self.register_manager.registers = parent_registers;
2419 parent_registers = .{};
24202432
2421 self.stack.deinit(self.gpa);2433 self.stack.deinit(self.gpa);
2422 self.stack = parent_stack;2434 self.stack = parent_stack;
src/register_manager.zig+43-50
...@@ -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 function21 /// 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 }
3333
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 }
7567
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 }
8476
85 return regs;77 return regs;
...@@ -88,13 +80,11 @@ pub fn RegisterManager(...@@ -88,13 +80,11 @@ pub fn RegisterManager(
88 }80 }
89 }81 }
9082
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 }
9687
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);
10090
...@@ -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]);
10797
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 }
119108
120 break :blk regs;109 break :blk regs;
121 };110 };
122 }111 }
123112
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 }
128116
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 }
155143
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 previously147 // 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);
166152
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 }
170166
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 specified172 /// Allocates the specified register with the specified
172 /// instruction. Spills the register if it is currently173 /// 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;
177177
178 if (!self.isRegFree(reg)) {178 if (!self.isRegFree(reg)) {
179 // Move the instruction that was previously there to a179 // 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;
194193
195 if (!self.isRegFree(reg)) {194 if (!self.isRegFree(reg)) {
196 // Move the instruction that was previously there to a195 // 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 }
204202
205 /// Allocates the specified register with the specified203 /// Allocates the specified register with the specified
206 /// instruction. Assumes that the register is free and no204 /// 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;
211208
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 }
215213
216 /// Marks the specified register as free214 /// 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;
219217
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();
248246
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 }
253250
...@@ -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));
275272
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));
307303
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));
311306
...@@ -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));
338333
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);
341335
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));
344338
345 // Spill r3339 // 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);
348341
349 std.testing.expect(!function.register_manager.isRegAllocated(.r2));342 std.testing.expect(!function.register_manager.isRegAllocated(.r2));