| ... | @@ -17,6 +17,10 @@ pub fn RegisterManager( | ... | @@ -17,6 +17,10 @@ pub fn RegisterManager( |
| 17 | comptime Register: type, | 17 | comptime Register: type, |
| 18 | comptime callee_preserved_regs: []const Register, | 18 | comptime callee_preserved_regs: []const Register, |
| 19 | ) type { | 19 | ) type { |
| | 20 | // architectures which do not have a concept of registers should |
| | 21 | // refrain from using RegisterManager |
| | 22 | assert(callee_preserved_regs.len > 0); // see note above |
| | 23 | |
| 20 | return struct { | 24 | return struct { |
| 21 | /// Tracks the AIR instruction allocated to every register or | 25 | /// Tracks the AIR instruction allocated to every register or |
| 22 | /// `null` if no instruction is allocated to a register | 26 | /// `null` if no instruction is allocated to a register |
| ... | @@ -45,17 +49,20 @@ pub fn RegisterManager( | ... | @@ -45,17 +49,20 @@ pub fn RegisterManager( |
| 45 | } | 49 | } |
| 46 | | 50 | |
| 47 | fn getRegisterMask(reg: Register) ?FreeRegInt { | 51 | fn getRegisterMask(reg: Register) ?FreeRegInt { |
| 48 | if (FreeRegInt == u0) return null; | | |
| 49 | const index = reg.allocIndex() orelse return null; | 52 | const index = reg.allocIndex() orelse return null; |
| 50 | const shift = @intCast(ShiftInt, index); | 53 | const shift = @intCast(ShiftInt, index); |
| 51 | const mask = @as(FreeRegInt, 1) << shift; | 54 | const mask = @as(FreeRegInt, 1) << shift; |
| 52 | return mask; | 55 | return mask; |
| 53 | } | 56 | } |
| 54 | | 57 | |
| | 58 | fn markRegAllocated(self: *Self, reg: Register) void { |
| | 59 | const mask = getRegisterMask(reg) orelse return; |
| | 60 | self.allocated_registers |= mask; |
| | 61 | } |
| | 62 | |
| 55 | fn markRegUsed(self: *Self, reg: Register) void { | 63 | fn markRegUsed(self: *Self, reg: Register) void { |
| 56 | const mask = getRegisterMask(reg) orelse return; | 64 | const mask = getRegisterMask(reg) orelse return; |
| 57 | self.free_registers &= ~mask; | 65 | self.free_registers &= ~mask; |
| 58 | self.allocated_registers |= mask; | | |
| 59 | } | 66 | } |
| 60 | | 67 | |
| 61 | fn markRegFree(self: *Self, reg: Register) void { | 68 | fn markRegFree(self: *Self, reg: Register) void { |
| ... | @@ -120,7 +127,6 @@ pub fn RegisterManager( | ... | @@ -120,7 +127,6 @@ pub fn RegisterManager( |
| 120 | insts: [count]?Air.Inst.Index, | 127 | insts: [count]?Air.Inst.Index, |
| 121 | exceptions: []const Register, | 128 | exceptions: []const Register, |
| 122 | ) ?[count]Register { | 129 | ) ?[count]Register { |
| 123 | comptime if (callee_preserved_regs.len == 0) return null; | | |
| 124 | comptime assert(count > 0 and count <= callee_preserved_regs.len); | 130 | comptime assert(count > 0 and count <= callee_preserved_regs.len); |
| 125 | assert(count + exceptions.len <= callee_preserved_regs.len); | 131 | assert(count + exceptions.len <= callee_preserved_regs.len); |
| 126 | | 132 | |
| ... | @@ -138,19 +144,20 @@ pub fn RegisterManager( | ... | @@ -138,19 +144,20 @@ pub fn RegisterManager( |
| 138 | i += 1; | 144 | i += 1; |
| 139 | } | 145 | } |
| 140 | } | 146 | } |
| | 147 | assert(i == count); |
| 141 | | 148 | |
| 142 | if (i == count) { | 149 | for (regs) |reg, j| { |
| 143 | for (regs) |reg, j| { | 150 | self.markRegAllocated(reg); |
| 144 | if (insts[j]) |inst| { | 151 | |
| 145 | // Track the register | 152 | if (insts[j]) |inst| { |
| 146 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null | 153 | // Track the register |
| 147 | self.registers[index] = inst; | 154 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null |
| 148 | self.markRegUsed(reg); | 155 | self.registers[index] = inst; |
| 149 | } | 156 | self.markRegUsed(reg); |
| 150 | } | 157 | } |
| | 158 | } |
| 151 | | 159 | |
| 152 | return regs; | 160 | return regs; |
| 153 | } else return null; | | |
| 154 | } | 161 | } |
| 155 | | 162 | |
| 156 | /// Allocates a register and optionally tracks it with a | 163 | /// Allocates a register and optionally tracks it with a |
| ... | @@ -188,8 +195,9 @@ pub fn RegisterManager( | ... | @@ -188,8 +195,9 @@ pub fn RegisterManager( |
| 188 | if (i >= count) break; | 195 | if (i >= count) break; |
| 189 | if (mem.indexOfScalar(Register, exceptions, reg) != null) continue; | 196 | if (mem.indexOfScalar(Register, exceptions, reg) != null) continue; |
| 190 | if (self.isRegFrozen(reg)) continue; | 197 | if (self.isRegFrozen(reg)) continue; |
| 191 | regs[i] = reg; | | |
| 192 | | 198 | |
| | 199 | regs[i] = reg; |
| | 200 | self.markRegAllocated(reg); |
| 193 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null | 201 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null |
| 194 | if (insts[i]) |inst| { | 202 | if (insts[i]) |inst| { |
| 195 | // Track the register | 203 | // Track the register |
| ... | @@ -233,6 +241,7 @@ pub fn RegisterManager( | ... | @@ -233,6 +241,7 @@ pub fn RegisterManager( |
| 233 | /// register. | 241 | /// register. |
| 234 | pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) !void { | 242 | pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) !void { |
| 235 | const index = reg.allocIndex() orelse return; | 243 | const index = reg.allocIndex() orelse return; |
| | 244 | self.markRegAllocated(reg); |
| 236 | | 245 | |
| 237 | if (inst) |tracked_inst| | 246 | if (inst) |tracked_inst| |
| 238 | if (!self.isRegFree(reg)) { | 247 | if (!self.isRegFree(reg)) { |
| ... | @@ -260,6 +269,7 @@ pub fn RegisterManager( | ... | @@ -260,6 +269,7 @@ pub fn RegisterManager( |
| 260 | /// spilling is necessary. | 269 | /// spilling is necessary. |
| 261 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void { | 270 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void { |
| 262 | const index = reg.allocIndex() orelse return; | 271 | const index = reg.allocIndex() orelse return; |
| | 272 | self.markRegAllocated(reg); |
| 263 | | 273 | |
| 264 | assert(self.registers[index] == null); | 274 | assert(self.registers[index] == null); |
| 265 | self.registers[index] = inst; | 275 | self.registers[index] = inst; |
| ... | @@ -424,6 +434,11 @@ test "tryAllocRegs" { | ... | @@ -424,6 +434,11 @@ test "tryAllocRegs" { |
| 424 | | 434 | |
| 425 | try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?); | 435 | try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?); |
| 426 | | 436 | |
| | 437 | try expect(function.register_manager.isRegAllocated(.r0)); |
| | 438 | try expect(function.register_manager.isRegAllocated(.r1)); |
| | 439 | try expect(function.register_manager.isRegAllocated(.r2)); |
| | 440 | try expect(!function.register_manager.isRegAllocated(.r3)); |
| | 441 | |
| 427 | // Exceptions | 442 | // Exceptions |
| 428 | // | 443 | // |
| 429 | // TODO deprecated, remove test once no backend uses exceptions | 444 | // TODO deprecated, remove test once no backend uses exceptions |
| ... | @@ -444,6 +459,11 @@ test "tryAllocRegs" { | ... | @@ -444,6 +459,11 @@ test "tryAllocRegs" { |
| 444 | try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?); | 459 | try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }, &.{}).?); |
| 445 | } | 460 | } |
| 446 | try expect(!function.register_manager.frozenRegsExist()); | 461 | try expect(!function.register_manager.frozenRegsExist()); |
| | 462 | |
| | 463 | try expect(function.register_manager.isRegAllocated(.r0)); |
| | 464 | try expect(function.register_manager.isRegAllocated(.r1)); |
| | 465 | try expect(function.register_manager.isRegAllocated(.r2)); |
| | 466 | try expect(function.register_manager.isRegAllocated(.r3)); |
| 447 | } | 467 | } |
| 448 | | 468 | |
| 449 | test "allocRegs" { | 469 | test "allocRegs" { |
| ... | @@ -462,6 +482,11 @@ test "allocRegs" { | ... | @@ -462,6 +482,11 @@ test "allocRegs" { |
| 462 | mock_instruction, | 482 | mock_instruction, |
| 463 | }, &.{})); | 483 | }, &.{})); |
| 464 | | 484 | |
| | 485 | try expect(function.register_manager.isRegAllocated(.r0)); |
| | 486 | try expect(function.register_manager.isRegAllocated(.r1)); |
| | 487 | try expect(function.register_manager.isRegAllocated(.r2)); |
| | 488 | try expect(!function.register_manager.isRegAllocated(.r3)); |
| | 489 | |
| 465 | // Exceptions | 490 | // Exceptions |
| 466 | // | 491 | // |
| 467 | // TODO deprecated, remove test once no backend uses exceptions | 492 | // TODO deprecated, remove test once no backend uses exceptions |
| ... | @@ -480,6 +505,11 @@ test "allocRegs" { | ... | @@ -480,6 +505,11 @@ test "allocRegs" { |
| 480 | try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }, &.{})); | 505 | try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, try function.register_manager.allocRegs(3, .{ null, null, null }, &.{})); |
| 481 | } | 506 | } |
| 482 | try expect(!function.register_manager.frozenRegsExist()); | 507 | try expect(!function.register_manager.frozenRegsExist()); |
| | 508 | |
| | 509 | try expect(function.register_manager.isRegAllocated(.r0)); |
| | 510 | try expect(function.register_manager.isRegAllocated(.r1)); |
| | 511 | try expect(function.register_manager.isRegAllocated(.r2)); |
| | 512 | try expect(function.register_manager.isRegAllocated(.r3)); |
| 483 | } | 513 | } |
| 484 | | 514 | |
| 485 | test "getReg" { | 515 | test "getReg" { |