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