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;
3131const InnerError = error{
3232 OutOfMemory,
3333 CodegenFail,
34 OutOfRegisters,
3435};
3536
3637gpa: Allocator,
......@@ -274,6 +275,9 @@ pub fn generate(
274275
275276 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
276277 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 },
277281 else => |e| return e,
278282 };
279283 defer call_info.deinit(&function);
......@@ -285,6 +289,9 @@ pub fn generate(
285289
286290 function.gen() catch |err| switch (err) {
287291 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 },
288295 else => |e| return e,
289296 };
290297
src/arch/arm/CodeGen.zig+7
......@@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
3131const InnerError = error{
3232 OutOfMemory,
3333 CodegenFail,
34 OutOfRegisters,
3435};
3536
3637gpa: Allocator,
......@@ -279,6 +280,9 @@ pub fn generate(
279280
280281 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
281282 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 },
282286 else => |e| return e,
283287 };
284288 defer call_info.deinit(&function);
......@@ -290,6 +294,9 @@ pub fn generate(
290294
291295 function.gen() catch |err| switch (err) {
292296 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 },
293300 else => |e| return e,
294301 };
295302
src/arch/riscv64/CodeGen.zig+7
......@@ -31,6 +31,7 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
3131const InnerError = error{
3232 OutOfMemory,
3333 CodegenFail,
34 OutOfRegisters,
3435};
3536
3637gpa: Allocator,
......@@ -280,6 +281,9 @@ pub fn generate(
280281
281282 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
282283 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 },
283287 else => |e| return e,
284288 };
285289 defer call_info.deinit(&function);
......@@ -291,6 +295,9 @@ pub fn generate(
291295
292296 function.gen() catch |err| switch (err) {
293297 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 },
294301 else => |e| return e,
295302 };
296303
src/arch/x86_64/CodeGen.zig+7
......@@ -31,6 +31,7 @@ const Zir = @import("../../Zir.zig");
3131const InnerError = error{
3232 OutOfMemory,
3333 CodegenFail,
34 OutOfRegisters,
3435};
3536
3637const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);
......@@ -308,6 +309,9 @@ pub fn generate(
308309
309310 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
310311 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 },
311315 else => |e| return e,
312316 };
313317 defer call_info.deinit(&function);
......@@ -319,6 +323,9 @@ pub fn generate(
319323
320324 function.gen() catch |err| switch (err) {
321325 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 },
322329 else => |e| return e,
323330 };
324331
src/register_manager.zig+70-27
......@@ -12,6 +12,17 @@ const expectEqualSlices = std.testing.expectEqualSlices;
1212
1313const 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
1526pub fn RegisterManager(
1627 comptime Function: type,
1728 comptime Register: type,
......@@ -168,8 +179,9 @@ pub fn RegisterManager(
168179 self: *Self,
169180 comptime count: comptime_int,
170181 insts: [count]?Air.Inst.Index,
171 ) ![count]Register {
182 ) AllocateRegistersError![count]Register {
172183 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
174186 const result = self.tryAllocRegs(count, insts) orelse blk: {
175187 // We'll take over the first count registers. Spill
......@@ -214,14 +226,14 @@ pub fn RegisterManager(
214226
215227 /// Allocates a register and optionally tracks it with a
216228 /// 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 {
218230 return (try self.allocRegs(1, .{inst}))[0];
219231 }
220232
221233 /// Spills the register if it is currently allocated. If a
222234 /// corresponding instruction is passed, will also track this
223235 /// 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 {
225237 const index = reg.allocIndex() orelse return;
226238 self.markRegAllocated(reg);
227239
......@@ -317,6 +329,13 @@ fn MockFunction(comptime Register: type) type {
317329 _ = inst;
318330 try self.spilled.append(self.allocator, reg);
319331 }
332
333 pub fn genAdd(self: *Self, res: Register, lhs: Register, rhs: Register) !void {
334 _ = self;
335 _ = res;
336 _ = lhs;
337 _ = rhs;
338 }
320339 };
321340}
322341
......@@ -431,7 +450,9 @@ test "tryAllocRegs" {
431450 try expect(function.register_manager.isRegAllocated(.r3));
432451}
433452
434test "allocRegs" {
453test "allocRegs: normal usage" {
454 // TODO: convert this into a decltest once that is supported
455
435456 const allocator = std.testing.allocator;
436457
437458 var function = MockFunction2{
......@@ -439,35 +460,57 @@ test "allocRegs" {
439460 };
440461 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, .{
445 mock_instruction,
446 mock_instruction,
447 mock_instruction,
448 }));
487test "allocRegs: selectively reducing register pressure" {
488 // TODO: convert this into a decltest once that is supported
449489
450 try expect(function.register_manager.isRegAllocated(.r0));
451 try expect(function.register_manager.isRegAllocated(.r1));
452 try expect(function.register_manager.isRegAllocated(.r2));
453 try expect(!function.register_manager.isRegAllocated(.r3));
490 const allocator = std.testing.allocator;
491
492 var function = MockFunction2{
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);
459497 {
460 function.register_manager.freezeRegs(&.{.r1});
461 defer function.register_manager.unfreezeRegs(&.{.r1});
498 const result_reg: MockRegister2 = .r1;
462499
463 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }));
464 }
465 try expect(!function.register_manager.frozenRegsExist());
500 function.register_manager.freezeRegs(&.{result_reg});
501 defer function.register_manager.unfreezeRegs(&.{result_reg});
466502
467 try expect(function.register_manager.isRegAllocated(.r0));
468 try expect(function.register_manager.isRegAllocated(.r1));
469 try expect(function.register_manager.isRegAllocated(.r2));
470 try expect(function.register_manager.isRegAllocated(.r3));
503 // Here, we don't defer unfreeze because we manually unfreeze
504 // after genAdd
505 const regs = try function.register_manager.allocRegs(2, .{ null, null });
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 }
471514}
472515
473516test "getReg" {