authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-01-26 11:47:39+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-26 18:23:27+01:00
log35503b3d3fe1bfce19f1ea3e78a75ce87b0ed646
tree0ca32ae96bd55966460f56df40443be64c3c0307
parent4192be84039cfac9ec458dc42370f67d700bd8ed

stage2 regalloc: Add freezeRegs/unfreezeRegs API

The freeze/unfreeze API replaces the exceptions API for hopefully preventing bugs in codegen code using the RegisterManager. The exceptions API is still available for backwards compatibility and will be removed once all backends transition to the new freeze/unfreeze API.

1 files changed, 114 insertions(+), 18 deletions(-)

src/register_manager.zig+114-18
...@@ -6,7 +6,6 @@ const Allocator = std.mem.Allocator;...@@ -6,7 +6,6 @@ const Allocator = std.mem.Allocator;
6const Air = @import("Air.zig");6const Air = @import("Air.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;
10const expect = std.testing.expect;9const expect = std.testing.expect;
11const expectEqual = std.testing.expectEqual;10const expectEqual = std.testing.expectEqual;
12const expectEqualSlices = std.testing.expectEqualSlices;11const expectEqualSlices = std.testing.expectEqualSlices;
...@@ -19,15 +18,25 @@ pub fn RegisterManager(...@@ -19,15 +18,25 @@ pub fn RegisterManager(
19 comptime callee_preserved_regs: []const Register,18 comptime callee_preserved_regs: []const Register,
20) type {19) type {
21 return struct {20 return struct {
21 /// Tracks the AIR instruction allocated to every register or
22 /// `null` if no instruction is allocated to a register
23 ///
22 /// The key must be canonical register.24 /// The key must be canonical register.
23 registers: [callee_preserved_regs.len]?Air.Inst.Index = [_]?Air.Inst.Index{null} ** callee_preserved_regs.len,25 registers: [callee_preserved_regs.len]?Air.Inst.Index = [_]?Air.Inst.Index{null} ** callee_preserved_regs.len,
26 /// Tracks which registers are free (in which case the
27 /// corresponding bit is set to 1)
24 free_registers: FreeRegInt = math.maxInt(FreeRegInt),28 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
25 /// Tracks all registers allocated in the course of this function29 /// Tracks all registers allocated in the course of this
30 /// function
26 allocated_registers: FreeRegInt = 0,31 allocated_registers: FreeRegInt = 0,
32 /// Tracks registers which are temporarily blocked from being
33 /// allocated
34 frozen_registers: FreeRegInt = 0,
2735
28 const Self = @This();36 const Self = @This();
2937
30 /// An integer whose bits represent all the registers and whether they are free.38 /// An integer whose bits represent all the registers and
39 /// whether they are free.
31 const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len);40 const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len);
32 const ShiftInt = math.Log2Int(FreeRegInt);41 const ShiftInt = math.Log2Int(FreeRegInt);
3342
...@@ -35,43 +44,76 @@ pub fn RegisterManager(...@@ -35,43 +44,76 @@ pub fn RegisterManager(
35 return @fieldParentPtr(Function, "register_manager", self);44 return @fieldParentPtr(Function, "register_manager", self);
36 }45 }
3746
38 fn markRegUsed(self: *Self, reg: Register) void {47 fn getRegisterMask(reg: Register) ?FreeRegInt {
39 if (FreeRegInt == u0) return;48 if (FreeRegInt == u0) return null;
40 const index = reg.allocIndex() orelse return;49 const index = reg.allocIndex() orelse return null;
41 const shift = @intCast(ShiftInt, index);50 const shift = @intCast(ShiftInt, index);
42 const mask = @as(FreeRegInt, 1) << shift;51 const mask = @as(FreeRegInt, 1) << shift;
52 return mask;
53 }
54
55 fn markRegUsed(self: *Self, reg: Register) void {
56 const mask = getRegisterMask(reg) orelse return;
43 self.free_registers &= ~mask;57 self.free_registers &= ~mask;
44 self.allocated_registers |= mask;58 self.allocated_registers |= mask;
45 }59 }
4660
47 fn markRegFree(self: *Self, reg: Register) void {61 fn markRegFree(self: *Self, reg: Register) void {
48 if (FreeRegInt == u0) return;62 const mask = getRegisterMask(reg) orelse return;
49 const index = reg.allocIndex() orelse return;63 self.free_registers |= mask;
50 const shift = @intCast(ShiftInt, index);
51 self.free_registers |= @as(FreeRegInt, 1) << shift;
52 }64 }
5365
54 /// Returns true when this register is not tracked66 /// Returns true when this register is not tracked
55 pub fn isRegFree(self: Self, reg: Register) bool {67 pub fn isRegFree(self: Self, reg: Register) bool {
56 if (FreeRegInt == u0) return true;68 const mask = getRegisterMask(reg) orelse return true;
57 const index = reg.allocIndex() orelse return true;69 return self.free_registers & mask != 0;
58 const shift = @intCast(ShiftInt, index);
59 return self.free_registers & @as(FreeRegInt, 1) << shift != 0;
60 }70 }
6171
62 /// Returns whether this register was allocated in the course72 /// Returns whether this register was allocated in the course
63 /// of this function.73 /// of this function.
74 ///
64 /// Returns false when this register is not tracked75 /// Returns false when this register is not tracked
65 pub fn isRegAllocated(self: Self, reg: Register) bool {76 pub fn isRegAllocated(self: Self, reg: Register) bool {
66 if (FreeRegInt == u0) return false;77 const mask = getRegisterMask(reg) orelse return false;
67 const index = reg.allocIndex() orelse return false;78 return self.allocated_registers & mask != 0;
68 const shift = @intCast(ShiftInt, index);79 }
69 return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;80
81 /// Returns whether this register is frozen
82 ///
83 /// Returns false when this register is not tracked
84 pub fn isRegFrozen(self: Self, reg: Register) bool {
85 const mask = getRegisterMask(reg) orelse return false;
86 return self.frozen_registers & mask != 0;
87 }
88
89 /// Prevents the registers from being allocated until they are
90 /// unfrozen again
91 pub fn freezeRegs(self: *Self, regs: []const Register) void {
92 for (regs) |reg| {
93 const mask = getRegisterMask(reg) orelse continue;
94 self.frozen_registers |= mask;
95 }
96 }
97
98 /// Enables the allocation of the registers
99 pub fn unfreezeRegs(self: *Self, regs: []const Register) void {
100 for (regs) |reg| {
101 const mask = getRegisterMask(reg) orelse continue;
102 self.frozen_registers &= ~mask;
103 }
104 }
105
106 /// Returns true when at least one register is frozen
107 pub fn frozenRegsExist(self: Self) bool {
108 return self.frozen_registers != 0;
70 }109 }
71110
72 /// Allocates a specified number of registers, optionally111 /// Allocates a specified number of registers, optionally
73 /// tracking them. Returns `null` if not enough registers are112 /// tracking them. Returns `null` if not enough registers are
74 /// free.113 /// free.
114 ///
115 /// Exceptions are deprecated, use freezeRegs and unfreezeRegs
116 /// instead.
75 pub fn tryAllocRegs(117 pub fn tryAllocRegs(
76 self: *Self,118 self: *Self,
77 comptime count: comptime_int,119 comptime count: comptime_int,
...@@ -90,6 +132,7 @@ pub fn RegisterManager(...@@ -90,6 +132,7 @@ pub fn RegisterManager(
90 for (callee_preserved_regs) |reg| {132 for (callee_preserved_regs) |reg| {
91 if (i >= count) break;133 if (i >= count) break;
92 if (mem.indexOfScalar(Register, exceptions, reg) != null) continue;134 if (mem.indexOfScalar(Register, exceptions, reg) != null) continue;
135 if (self.isRegFrozen(reg)) continue;
93 if (self.isRegFree(reg)) {136 if (self.isRegFree(reg)) {
94 regs[i] = reg;137 regs[i] = reg;
95 i += 1;138 i += 1;
...@@ -113,6 +156,9 @@ pub fn RegisterManager(...@@ -113,6 +156,9 @@ pub fn RegisterManager(
113 /// Allocates a register and optionally tracks it with a156 /// Allocates a register and optionally tracks it with a
114 /// corresponding instruction. Returns `null` if all registers157 /// corresponding instruction. Returns `null` if all registers
115 /// are allocated.158 /// are allocated.
159 ///
160 /// Exceptions are deprecated, use freezeRegs and unfreezeRegs
161 /// instead.
116 pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, exceptions: []const Register) ?Register {162 pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, exceptions: []const Register) ?Register {
117 return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null;163 return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null;
118 }164 }
...@@ -120,6 +166,9 @@ pub fn RegisterManager(...@@ -120,6 +166,9 @@ pub fn RegisterManager(
120 /// Allocates a specified number of registers, optionally166 /// Allocates a specified number of registers, optionally
121 /// tracking them. Asserts that count + exceptions.len is not167 /// tracking them. Asserts that count + exceptions.len is not
122 /// larger than the total number of registers available.168 /// larger than the total number of registers available.
169 ///
170 /// Exceptions are deprecated, use freezeRegs and unfreezeRegs
171 /// instead.
123 pub fn allocRegs(172 pub fn allocRegs(
124 self: *Self,173 self: *Self,
125 comptime count: comptime_int,174 comptime count: comptime_int,
...@@ -138,6 +187,7 @@ pub fn RegisterManager(...@@ -138,6 +187,7 @@ pub fn RegisterManager(
138 for (callee_preserved_regs) |reg| {187 for (callee_preserved_regs) |reg| {
139 if (i >= count) break;188 if (i >= count) break;
140 if (mem.indexOfScalar(Register, exceptions, reg) != null) continue;189 if (mem.indexOfScalar(Register, exceptions, reg) != null) continue;
190 if (self.isRegFrozen(reg)) continue;
141 regs[i] = reg;191 regs[i] = reg;
142192
143 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null193 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
...@@ -171,6 +221,9 @@ pub fn RegisterManager(...@@ -171,6 +221,9 @@ pub fn RegisterManager(
171221
172 /// Allocates a register and optionally tracks it with a222 /// Allocates a register and optionally tracks it with a
173 /// corresponding instruction.223 /// corresponding instruction.
224 ///
225 /// Exceptions are deprecated, use freezeRegs and unfreezeRegs
226 /// instead.
174 pub fn allocReg(self: *Self, inst: ?Air.Inst.Index, exceptions: []const Register) !Register {227 pub fn allocReg(self: *Self, inst: ?Air.Inst.Index, exceptions: []const Register) !Register {
175 return (try self.allocRegs(1, .{inst}, exceptions))[0];228 return (try self.allocRegs(1, .{inst}, exceptions))[0];
176 }229 }
...@@ -343,9 +396,22 @@ test "allocReg: spilling" {...@@ -343,9 +396,22 @@ test "allocReg: spilling" {
343 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);396 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
344397
345 // Exceptions398 // Exceptions
399 //
400 // TODO deprecated, remove test once no backend uses exceptions
401 // anymore
346 function.register_manager.freeReg(.r2);402 function.register_manager.freeReg(.r2);
347 function.register_manager.freeReg(.r3);403 function.register_manager.freeReg(.r3);
348 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, &.{.r2}));404 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, &.{.r2}));
405
406 // Frozen registers
407 function.register_manager.freeReg(.r3);
408 {
409 function.register_manager.freezeRegs(&.{.r2});
410 defer function.register_manager.unfreezeRegs(&.{.r2});
411
412 try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction, &.{}));
413 }
414 try expect(!function.register_manager.frozenRegsExist());
349}415}
350416
351test "tryAllocRegs" {417test "tryAllocRegs" {
...@@ -359,10 +425,25 @@ test "tryAllocRegs" {...@@ -359,10 +425,25 @@ test "tryAllocRegs" {
359 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?);425 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?);
360426
361 // Exceptions427 // Exceptions
428 //
429 // TODO deprecated, remove test once no backend uses exceptions
430 // anymore
362 function.register_manager.freeReg(.r0);431 function.register_manager.freeReg(.r0);
363 function.register_manager.freeReg(.r1);432 function.register_manager.freeReg(.r1);
364 function.register_manager.freeReg(.r2);433 function.register_manager.freeReg(.r2);
365 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{.r1}).?);434 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{.r1}).?);
435
436 // Frozen registers
437 function.register_manager.freeReg(.r0);
438 function.register_manager.freeReg(.r2);
439 function.register_manager.freeReg(.r3);
440 {
441 function.register_manager.freezeRegs(&.{.r1});
442 defer function.register_manager.unfreezeRegs(&.{.r1});
443
444 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?);
445 }
446 try expect(!function.register_manager.frozenRegsExist());
366}447}
367448
368test "allocRegs" {449test "allocRegs" {
...@@ -382,8 +463,23 @@ test "allocRegs" {...@@ -382,8 +463,23 @@ test "allocRegs" {
382 }, &.{}));463 }, &.{}));
383464
384 // Exceptions465 // Exceptions
466 //
467 // TODO deprecated, remove test once no backend uses exceptions
468 // anymore
385 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }, &.{.r1}));469 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }, &.{.r1}));
386 try expectEqualSlices(MockRegister2, &[_]MockRegister2{ .r0, .r2 }, function.spilled.items);470 try expectEqualSlices(MockRegister2, &[_]MockRegister2{ .r0, .r2 }, function.spilled.items);
471
472 // Frozen registers
473 function.register_manager.freeReg(.r0);
474 function.register_manager.freeReg(.r2);
475 function.register_manager.freeReg(.r3);
476 {
477 function.register_manager.freezeRegs(&.{.r1});
478 defer function.register_manager.unfreezeRegs(&.{.r1});
479
480 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }, &.{}));
481 }
482 try expect(!function.register_manager.frozenRegsExist());
387}483}
388484
389test "getReg" {485test "getReg" {