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 {...@@ -952,7 +952,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
952 /// allocated. A second call to `copyToTmpRegister` may return the same register.952 /// allocated. A second call to `copyToTmpRegister` may return the same register.
953 /// This can have a side effect of spilling instructions to the stack to free up a register.953 /// This can have a side effect of spilling instructions to the stack to free up a register.
954 fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register {954 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, &.{});
956 try self.genSetReg(src, ty, reg, mcv);956 try self.genSetReg(src, ty, reg, mcv);
957 return reg;957 return reg;
958 }958 }
...@@ -2228,7 +2228,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2228,7 +2228,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2228 switch (mc_arg) {2228 switch (mc_arg) {
2229 .none => continue,2229 .none => continue,
2230 .register => |reg| {2230 .register => |reg| {
2231 try self.register_manager.getRegWithoutTracking(reg);2231 try self.register_manager.getReg(reg, null);
2232 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2232 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2233 },2233 },
2234 .stack_offset => {2234 .stack_offset => {
...@@ -2370,7 +2370,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2370,7 +2370,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2370 .compare_flags_signed => unreachable,2370 .compare_flags_signed => unreachable,
2371 .compare_flags_unsigned => unreachable,2371 .compare_flags_unsigned => unreachable,
2372 .register => |reg| {2372 .register => |reg| {
2373 try self.register_manager.getRegWithoutTracking(reg);2373 try self.register_manager.getReg(reg, null);
2374 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2374 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2375 },2375 },
2376 .stack_offset => {2376 .stack_offset => {
...@@ -2433,7 +2433,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2433,7 +2433,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2433 .compare_flags_signed => unreachable,2433 .compare_flags_signed => unreachable,
2434 .compare_flags_unsigned => unreachable,2434 .compare_flags_unsigned => unreachable,
2435 .register => |reg| {2435 .register => |reg| {
2436 try self.register_manager.getRegWithoutTracking(reg);2436 try self.register_manager.getReg(reg, null);
2437 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2437 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2438 },2438 },
2439 .stack_offset => {2439 .stack_offset => {
...@@ -2486,7 +2486,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2486,7 +2486,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2486 .register => |reg| {2486 .register => |reg| {
2487 // TODO prevent this macho if block to be generated for all archs2487 // TODO prevent this macho if block to be generated for all archs
2488 switch (arch) {2488 switch (arch) {
2489 .x86_64, .aarch64 => try self.register_manager.getRegWithoutTracking(reg),2489 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),
2490 else => unreachable,2490 else => unreachable,
2491 }2491 }
2492 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2492 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
...@@ -3190,7 +3190,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3190,7 +3190,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31903190
3191 const arg = inst.args[i];3191 const arg = inst.args[i];
3192 const arg_mcv = try self.resolveInst(arg);3192 const arg_mcv = try self.resolveInst(arg);
3193 try self.register_manager.getRegWithoutTracking(reg);3193 try self.register_manager.getReg(reg, null);
3194 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3194 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
3195 }3195 }
31963196
...@@ -3223,7 +3223,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3223,7 +3223,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32233223
3224 const arg = inst.args[i];3224 const arg = inst.args[i];
3225 const arg_mcv = try self.resolveInst(arg);3225 const arg_mcv = try self.resolveInst(arg);
3226 try self.register_manager.getRegWithoutTracking(reg);3226 try self.register_manager.getReg(reg, null);
3227 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3227 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
3228 }3228 }
32293229
...@@ -3258,7 +3258,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3258,7 +3258,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32583258
3259 const arg = inst.args[i];3259 const arg = inst.args[i];
3260 const arg_mcv = try self.resolveInst(arg);3260 const arg_mcv = try self.resolveInst(arg);
3261 try self.register_manager.getRegWithoutTracking(reg);3261 try self.register_manager.getReg(reg, null);
3262 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3262 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
3263 }3263 }
32643264
...@@ -3291,7 +3291,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3291,7 +3291,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32913291
3292 const arg = inst.args[i];3292 const arg = inst.args[i];
3293 const arg_mcv = try self.resolveInst(arg);3293 const arg_mcv = try self.resolveInst(arg);
3294 try self.register_manager.getRegWithoutTracking(reg);3294 try self.register_manager.getReg(reg, null);
3295 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3295 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
3296 }3296 }
32973297
src/register_manager.zig+219-156
...@@ -7,6 +7,9 @@ const ir = @import("ir.zig");...@@ -7,6 +7,9 @@ const ir = @import("ir.zig");
7const Type = @import("type.zig").Type;7const Type = @import("type.zig").Type;
8const Module = @import("Module.zig");8const Module = @import("Module.zig");
9const LazySrcLoc = Module.LazySrcLoc;9const LazySrcLoc = Module.LazySrcLoc;
10const expect = std.testing.expect;
11const expectEqual = std.testing.expectEqual;
12const expectEqualSlices = std.testing.expectEqualSlices;
1013
11const log = std.log.scoped(.register_manager);14const log = std.log.scoped(.register_manager);
1215
...@@ -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 }
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.
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 }
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;
160 }111 }
161112
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 }
167119
168 /// Does not track the registers120 /// 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. Spill133 // We'll take over the first count registers. Spill
172 // the instructions that were previously there to a134 // 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;
180142
181 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null143 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 }
188161
189 i += 1;162 i += 1;
...@@ -193,39 +166,36 @@ pub fn RegisterManager(...@@ -193,39 +166,36 @@ pub fn RegisterManager(
193 };166 };
194 }167 }
195168
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 }
217174
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;
222180
223 if (!self.isRegFree(reg)) {181 if (inst) |tracked_inst|
224 // Move the instruction that was previously there to a182 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 }
231201
...@@ -250,42 +220,63 @@ pub fn RegisterManager(...@@ -250,42 +220,63 @@ pub fn RegisterManager(
250 };220 };
251}221}
252222
253const MockRegister = enum(u2) {223const MockRegister1 = enum(u2) {
254 r0,224 r0,
255 r1,225 r1,
256 r2,226 r2,
257 r3,227 r3,
258228
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
267const mock_callee_preserved_regs = [_]MockRegister{ .r2, .r3 };
268235
269const 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) = .{},
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 {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 }
279251
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};
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" {
286 const allocator = std.testing.allocator;277 const allocator = std.testing.allocator;
287278
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 };
298289
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
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, &.{}));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, &.{}));
305313
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));
308318
309 function.register_manager.freeReg(.r2);319 function.register_manager.freeReg(.r2);
310 function.register_manager.freeReg(.r3);320 function.register_manager.freeReg(.r3);
311321
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}
315327
316test "allocReg: spilling" {328test "allocReg: spilling" {
317 const allocator = std.testing.allocator;329 const allocator = std.testing.allocator;
318330
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 };
329341
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, &.{}));
335344
336 // Spill a register345 // 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);
339348
340 // No spilling necessary349 // 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}
345359
346test "getReg" {360test "tryAllocRegs" {
347 const allocator = std.testing.allocator;361 const allocator = std.testing.allocator;
348362
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 };
359373
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
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
363 try function.register_manager.getReg(.r3, &mock_instruction);422 try function.register_manager.getReg(.r3, &mock_instruction);
364423
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));
367428
368 // Spill r3429 // Spill r3
369 try function.register_manager.getReg(.r3, &mock_instruction);430 try function.register_manager.getReg(.r3, &mock_instruction);
370431
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}