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;
66const Air = @import("Air.zig");
77const Type = @import("type.zig").Type;
88const Module = @import("Module.zig");
9const LazySrcLoc = Module.LazySrcLoc;
109const expect = std.testing.expect;
1110const expectEqual = std.testing.expectEqual;
1211const expectEqualSlices = std.testing.expectEqualSlices;
......@@ -19,15 +18,25 @@ pub fn RegisterManager(
1918 comptime callee_preserved_regs: []const Register,
2019) type {
2120 return struct {
21 /// Tracks the AIR instruction allocated to every register or
22 /// `null` if no instruction is allocated to a register
23 ///
2224 /// The key must be canonical register.
2325 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)
2428 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
25 /// Tracks all registers allocated in the course of this function
29 /// Tracks all registers allocated in the course of this
30 /// function
2631 allocated_registers: FreeRegInt = 0,
32 /// Tracks registers which are temporarily blocked from being
33 /// allocated
34 frozen_registers: FreeRegInt = 0,
2735
2836 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.
3140 const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len);
3241 const ShiftInt = math.Log2Int(FreeRegInt);
3342
......@@ -35,43 +44,76 @@ pub fn RegisterManager(
3544 return @fieldParentPtr(Function, "register_manager", self);
3645 }
3746
38 fn markRegUsed(self: *Self, reg: Register) void {
39 if (FreeRegInt == u0) return;
40 const index = reg.allocIndex() orelse return;
47 fn getRegisterMask(reg: Register) ?FreeRegInt {
48 if (FreeRegInt == u0) return null;
49 const index = reg.allocIndex() orelse return null;
4150 const shift = @intCast(ShiftInt, index);
4251 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;
4357 self.free_registers &= ~mask;
4458 self.allocated_registers |= mask;
4559 }
4660
4761 fn markRegFree(self: *Self, reg: Register) void {
48 if (FreeRegInt == u0) return;
49 const index = reg.allocIndex() orelse return;
50 const shift = @intCast(ShiftInt, index);
51 self.free_registers |= @as(FreeRegInt, 1) << shift;
62 const mask = getRegisterMask(reg) orelse return;
63 self.free_registers |= mask;
5264 }
5365
5466 /// Returns true when this register is not tracked
5567 pub fn isRegFree(self: Self, reg: Register) bool {
56 if (FreeRegInt == u0) return true;
57 const index = reg.allocIndex() orelse return true;
58 const shift = @intCast(ShiftInt, index);
59 return self.free_registers & @as(FreeRegInt, 1) << shift != 0;
68 const mask = getRegisterMask(reg) orelse return true;
69 return self.free_registers & mask != 0;
6070 }
6171
6272 /// Returns whether this register was allocated in the course
6373 /// of this function.
74 ///
6475 /// Returns false when this register is not tracked
6576 pub fn isRegAllocated(self: Self, reg: Register) bool {
66 if (FreeRegInt == u0) return false;
67 const index = reg.allocIndex() orelse return false;
68 const shift = @intCast(ShiftInt, index);
69 return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;
77 const mask = getRegisterMask(reg) orelse return false;
78 return self.allocated_registers & mask != 0;
79 }
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;
70109 }
71110
72111 /// Allocates a specified number of registers, optionally
73112 /// tracking them. Returns `null` if not enough registers are
74113 /// free.
114 ///
115 /// Exceptions are deprecated, use freezeRegs and unfreezeRegs
116 /// instead.
75117 pub fn tryAllocRegs(
76118 self: *Self,
77119 comptime count: comptime_int,
......@@ -90,6 +132,7 @@ pub fn RegisterManager(
90132 for (callee_preserved_regs) |reg| {
91133 if (i >= count) break;
92134 if (mem.indexOfScalar(Register, exceptions, reg) != null) continue;
135 if (self.isRegFrozen(reg)) continue;
93136 if (self.isRegFree(reg)) {
94137 regs[i] = reg;
95138 i += 1;
......@@ -113,6 +156,9 @@ pub fn RegisterManager(
113156 /// Allocates a register and optionally tracks it with a
114157 /// corresponding instruction. Returns `null` if all registers
115158 /// are allocated.
159 ///
160 /// Exceptions are deprecated, use freezeRegs and unfreezeRegs
161 /// instead.
116162 pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, exceptions: []const Register) ?Register {
117163 return if (tryAllocRegs(self, 1, .{inst}, exceptions)) |regs| regs[0] else null;
118164 }
......@@ -120,6 +166,9 @@ pub fn RegisterManager(
120166 /// Allocates a specified number of registers, optionally
121167 /// tracking them. Asserts that count + exceptions.len is not
122168 /// larger than the total number of registers available.
169 ///
170 /// Exceptions are deprecated, use freezeRegs and unfreezeRegs
171 /// instead.
123172 pub fn allocRegs(
124173 self: *Self,
125174 comptime count: comptime_int,
......@@ -138,6 +187,7 @@ pub fn RegisterManager(
138187 for (callee_preserved_regs) |reg| {
139188 if (i >= count) break;
140189 if (mem.indexOfScalar(Register, exceptions, reg) != null) continue;
190 if (self.isRegFrozen(reg)) continue;
141191 regs[i] = reg;
142192
143193 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
......@@ -171,6 +221,9 @@ pub fn RegisterManager(
171221
172222 /// Allocates a register and optionally tracks it with a
173223 /// corresponding instruction.
224 ///
225 /// Exceptions are deprecated, use freezeRegs and unfreezeRegs
226 /// instead.
174227 pub fn allocReg(self: *Self, inst: ?Air.Inst.Index, exceptions: []const Register) !Register {
175228 return (try self.allocRegs(1, .{inst}, exceptions))[0];
176229 }
......@@ -343,9 +396,22 @@ test "allocReg: spilling" {
343396 try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
344397
345398 // Exceptions
399 //
400 // TODO deprecated, remove test once no backend uses exceptions
401 // anymore
346402 function.register_manager.freeReg(.r2);
347403 function.register_manager.freeReg(.r3);
348404 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());
349415}
350416
351417test "tryAllocRegs" {
......@@ -359,10 +425,25 @@ test "tryAllocRegs" {
359425 try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?);
360426
361427 // Exceptions
428 //
429 // TODO deprecated, remove test once no backend uses exceptions
430 // anymore
362431 function.register_manager.freeReg(.r0);
363432 function.register_manager.freeReg(.r1);
364433 function.register_manager.freeReg(.r2);
365434 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());
366447}
367448
368449test "allocRegs" {
......@@ -382,8 +463,23 @@ test "allocRegs" {
382463 }, &.{}));
383464
384465 // Exceptions
466 //
467 // TODO deprecated, remove test once no backend uses exceptions
468 // anymore
385469 try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }, &.{.r1}));
386470 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());
387483}
388484
389485test "getReg" {