authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-02-05 17:13:11+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-06 00:14:48+01:00
log4b3b487627d71fa082b0316383344b49c95bab0e
tree39d03d14a6b0bd4dca62d0994ec7b7d14d1ef0ce
parentd4c3475f3dce8ecf81ba2baaf97b6d25426919ec

stage2 regalloc: Introduce error.OutOfRegisters


5 files changed, 98 insertions(+), 27 deletions(-)

src/arch/aarch64/CodeGen.zig+7
...@@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;...@@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
31const InnerError = error{31const InnerError = error{
32 OutOfMemory,32 OutOfMemory,
33 CodegenFail,33 CodegenFail,
34 OutOfRegisters,
34};35};
3536
36gpa: Allocator,37gpa: Allocator,
...@@ -274,6 +275,9 @@ pub fn generate(...@@ -274,6 +275,9 @@ pub fn generate(
274275
275 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {276 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
276 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },277 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
278 error.OutOfRegisters => return FnResult{
279 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
280 },
277 else => |e| return e,281 else => |e| return e,
278 };282 };
279 defer call_info.deinit(&function);283 defer call_info.deinit(&function);
...@@ -285,6 +289,9 @@ pub fn generate(...@@ -285,6 +289,9 @@ pub fn generate(
285289
286 function.gen() catch |err| switch (err) {290 function.gen() catch |err| switch (err) {
287 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },291 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
292 error.OutOfRegisters => return FnResult{
293 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
294 },
288 else => |e| return e,295 else => |e| return e,
289 };296 };
290297
src/arch/arm/CodeGen.zig+7
...@@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;...@@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
31const InnerError = error{31const InnerError = error{
32 OutOfMemory,32 OutOfMemory,
33 CodegenFail,33 CodegenFail,
34 OutOfRegisters,
34};35};
3536
36gpa: Allocator,37gpa: Allocator,
...@@ -279,6 +280,9 @@ pub fn generate(...@@ -279,6 +280,9 @@ pub fn generate(
279280
280 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {281 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
281 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },282 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
283 error.OutOfRegisters => return FnResult{
284 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
285 },
282 else => |e| return e,286 else => |e| return e,
283 };287 };
284 defer call_info.deinit(&function);288 defer call_info.deinit(&function);
...@@ -290,6 +294,9 @@ pub fn generate(...@@ -290,6 +294,9 @@ pub fn generate(
290294
291 function.gen() catch |err| switch (err) {295 function.gen() catch |err| switch (err) {
292 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },296 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
297 error.OutOfRegisters => return FnResult{
298 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
299 },
293 else => |e| return e,300 else => |e| return e,
294 };301 };
295302
src/arch/riscv64/CodeGen.zig+7
...@@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;...@@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
31const InnerError = error{31const InnerError = error{
32 OutOfMemory,32 OutOfMemory,
33 CodegenFail,33 CodegenFail,
34 OutOfRegisters,
34};35};
3536
36gpa: Allocator,37gpa: Allocator,
...@@ -280,6 +281,9 @@ pub fn generate(...@@ -280,6 +281,9 @@ pub fn generate(
280281
281 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {282 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
282 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },283 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
284 error.OutOfRegisters => return FnResult{
285 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
286 },
283 else => |e| return e,287 else => |e| return e,
284 };288 };
285 defer call_info.deinit(&function);289 defer call_info.deinit(&function);
...@@ -291,6 +295,9 @@ pub fn generate(...@@ -291,6 +295,9 @@ pub fn generate(
291295
292 function.gen() catch |err| switch (err) {296 function.gen() catch |err| switch (err) {
293 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },297 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
298 error.OutOfRegisters => return FnResult{
299 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
300 },
294 else => |e| return e,301 else => |e| return e,
295 };302 };
296303
src/arch/x86_64/CodeGen.zig+7
...@@ -31,6 +31,7 @@ const Zir = @import("../../Zir.zig");...@@ -31,6 +31,7 @@ const Zir = @import("../../Zir.zig");
31const InnerError = error{31const InnerError = error{
32 OutOfMemory,32 OutOfMemory,
33 CodegenFail,33 CodegenFail,
34 OutOfRegisters,
34};35};
3536
36const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);37const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);
...@@ -308,6 +309,9 @@ pub fn generate(...@@ -308,6 +309,9 @@ pub fn generate(
308309
309 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {310 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
310 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },311 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
312 error.OutOfRegisters => return FnResult{
313 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
314 },
311 else => |e| return e,315 else => |e| return e,
312 };316 };
313 defer call_info.deinit(&function);317 defer call_info.deinit(&function);
...@@ -319,6 +323,9 @@ pub fn generate(...@@ -319,6 +323,9 @@ pub fn generate(
319323
320 function.gen() catch |err| switch (err) {324 function.gen() catch |err| switch (err) {
321 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },325 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
326 error.OutOfRegisters => return FnResult{
327 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
328 },
322 else => |e| return e,329 else => |e| return e,
323 };330 };
324331
src/register_manager.zig+70-27
...@@ -12,6 +12,17 @@ const expectEqualSlices = std.testing.expectEqualSlices;...@@ -12,6 +12,17 @@ const expectEqualSlices = std.testing.expectEqualSlices;
1212
13const log = std.log.scoped(.register_manager);13const log = std.log.scoped(.register_manager);
1414
15pub const AllocateRegistersError = error{
16 /// No registers are available anymore
17 OutOfRegisters,
18 /// Can happen when spilling an instruction in codegen runs out of
19 /// memory, so we propagate that error
20 OutOfMemory,
21 /// Can happen when spilling an instruction triggers a codegen
22 /// error, so we propagate that error
23 CodegenFail,
24};
25
15pub fn RegisterManager(26pub fn RegisterManager(
16 comptime Function: type,27 comptime Function: type,
17 comptime Register: type,28 comptime Register: type,
...@@ -168,8 +179,9 @@ pub fn RegisterManager(...@@ -168,8 +179,9 @@ pub fn RegisterManager(
168 self: *Self,179 self: *Self,
169 comptime count: comptime_int,180 comptime count: comptime_int,
170 insts: [count]?Air.Inst.Index,181 insts: [count]?Air.Inst.Index,
171 ) ![count]Register {182 ) AllocateRegistersError![count]Register {
172 comptime assert(count > 0 and count <= callee_preserved_regs.len);183 comptime assert(count > 0 and count <= callee_preserved_regs.len);
184 if (count > callee_preserved_regs.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters;
173185
174 const result = self.tryAllocRegs(count, insts) orelse blk: {186 const result = self.tryAllocRegs(count, insts) orelse blk: {
175 // We'll take over the first count registers. Spill187 // We'll take over the first count registers. Spill
...@@ -214,14 +226,14 @@ pub fn RegisterManager(...@@ -214,14 +226,14 @@ pub fn RegisterManager(
214226
215 /// Allocates a register and optionally tracks it with a227 /// Allocates a register and optionally tracks it with a
216 /// corresponding instruction.228 /// corresponding instruction.
217 pub fn allocReg(self: *Self, inst: ?Air.Inst.Index) !Register {229 pub fn allocReg(self: *Self, inst: ?Air.Inst.Index) AllocateRegistersError!Register {
218 return (try self.allocRegs(1, .{inst}))[0];230 return (try self.allocRegs(1, .{inst}))[0];
219 }231 }
220232
221 /// Spills the register if it is currently allocated. If a233 /// Spills the register if it is currently allocated. If a
222 /// corresponding instruction is passed, will also track this234 /// corresponding instruction is passed, will also track this
223 /// register.235 /// register.
224 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) !void {236 pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void {
225 const index = reg.allocIndex() orelse return;237 const index = reg.allocIndex() orelse return;
226 self.markRegAllocated(reg);238 self.markRegAllocated(reg);
227239
...@@ -317,6 +329,13 @@ fn MockFunction(comptime Register: type) type {...@@ -317,6 +329,13 @@ fn MockFunction(comptime Register: type) type {
317 _ = inst;329 _ = inst;
318 try self.spilled.append(self.allocator, reg);330 try self.spilled.append(self.allocator, reg);
319 }331 }
332
333 pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void {
334 _ = self;
335 _ = res;
336 _ = lhs;
337 _ = rhs;
338 }
320 };339 };
321}340}
322341
...@@ -431,7 +450,9 @@ test "tryAllocRegs" {...@@ -431,7 +450,9 @@ test "tryAllocRegs" {
431 try expect(function.register_manager.isRegAllocated(.r3));450 try expect(function.register_manager.isRegAllocated(.r3));
432}451}
433452
434test "allocRegs" {453test "allocRegs: normal usage" {
454 // TODO: convert this into a decltest once that is supported
455
435 const allocator = std.testing.allocator;456 const allocator = std.testing.allocator;
436457
437 var function = MockFunction2{458 var function = MockFunction2{
...@@ -439,35 +460,57 @@ test "allocRegs" {...@@ -439,35 +460,57 @@ test "allocRegs" {
439 };460 };
440 defer function.deinit();461 defer function.deinit();
441462
442 const mock_instruction: Air.Inst.Index = 1;463 {
464 const result_reg: MockRegister2 = .r1;
465
466 // The result register is known and fixed at this point, we
467 // don't want to accidentally allocate lhs or rhs to the
468 // result register, this is why we freeze it.
469 //
470 // Using defer unfreeze right after freeze is a good idea in
471 // most cases as you probably are using the frozen registers
472 // in the remainder of this scope and don't need to use it
473 // after the end of this scope. However, in some situations,
474 // it may make sense to manually unfreeze registers before the
475 // end of the scope when you are certain that they don't
476 // contain any valuable data anymore and can be reused. For an
477 // example of that, see `selectively reducing register
478 // pressure`.
479 function.register_manager.freezeRegs(&.{result_reg});
480 defer function.register_manager.unfreezeRegs(&.{result_reg});
481
482 const regs = try function.register_manager.allocRegs(2, .{ null, null });
483 try function.genAdd(result_reg, regs[0], regs[1]);
484 }
485}
443486
444 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, try function.register_manager.allocRegs(3, .{487test "allocRegs: selectively reducing register pressure" {
445 mock_instruction,488 // TODO: convert this into a decltest once that is supported
446 mock_instruction,
447 mock_instruction,
448 }));
449489
450 try expect(function.register_manager.isRegAllocated(.r0));490 const allocator = std.testing.allocator;
451 try expect(function.register_manager.isRegAllocated(.r1));491
452 try expect(function.register_manager.isRegAllocated(.r2));492 var function = MockFunction2{
453 try expect(!function.register_manager.isRegAllocated(.r3));493 .allocator = allocator,
494 };
495 defer function.deinit();
454496
455 // Frozen registers
456 function.register_manager.freeReg(.r0);
457 function.register_manager.freeReg(.r2);
458 function.register_manager.freeReg(.r3);
459 {497 {
460 function.register_manager.freezeRegs(&.{.r1});498 const result_reg: MockRegister2 = .r1;
461 defer function.register_manager.unfreezeRegs(&.{.r1});
462499
463 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }));500 function.register_manager.freezeRegs(&.{result_reg});
464 }501 defer function.register_manager.unfreezeRegs(&.{result_reg});
465 try expect(!function.register_manager.frozenRegsExist());
466502
467 try expect(function.register_manager.isRegAllocated(.r0));503 // Here, we don't defer unfreeze because we manually unfreeze
468 try expect(function.register_manager.isRegAllocated(.r1));504 // after genAdd
469 try expect(function.register_manager.isRegAllocated(.r2));505 const regs = try function.register_manager.allocRegs(2, .{ null, null });
470 try expect(function.register_manager.isRegAllocated(.r3));506 function.register_manager.freezeRegs(&.{result_reg});
507
508 try function.genAdd(result_reg, regs[0], regs[1]);
509 function.register_manager.unfreezeRegs(&regs);
510
511 const extra_summand_reg = try function.register_manager.allocReg(null);
512 try function.genAdd(result_reg, result_reg, extra_summand_reg);
513 }
471}514}
472515
473test "getReg" {516test "getReg" {