| ... | ... | @@ -55,6 +55,7 @@ pub fn RegisterManager( |
| 55 | 55 | const Self = @This(); |
| 56 | 56 | |
| 57 | 57 | pub const TrackedRegisters = [tracked_registers.len]Air.Inst.Index; |
| 58 | pub const TrackedIndex = std.math.IntFittingRange(0, tracked_registers.len - 1); |
| 58 | 59 | pub const RegisterBitSet = StaticBitSet(tracked_registers.len); |
| 59 | 60 | |
| 60 | 61 | fn getFunction(self: *Self) *Function { |
| ... | ... | @@ -66,45 +67,64 @@ pub fn RegisterManager( |
| 66 | 67 | return !register_class.isSet(index); |
| 67 | 68 | } |
| 68 | 69 | |
| 70 | fn markRegIndexAllocated(self: *Self, tracked_index: TrackedIndex) void { |
| 71 | self.allocated_registers.set(tracked_index); |
| 72 | } |
| 69 | 73 | fn markRegAllocated(self: *Self, reg: Register) void { |
| 70 | | const index = indexOfRegIntoTracked(reg) orelse return; |
| 71 | | self.allocated_registers.set(index); |
| 74 | self.markRegIndexAllocated(indexOfRegIntoTracked(reg) orelse return); |
| 72 | 75 | } |
| 73 | 76 | |
| 77 | fn markRegIndexUsed(self: *Self, tracked_index: TrackedIndex) void { |
| 78 | self.free_registers.unset(tracked_index); |
| 79 | } |
| 74 | 80 | fn markRegUsed(self: *Self, reg: Register) void { |
| 75 | | const index = indexOfRegIntoTracked(reg) orelse return; |
| 76 | | self.free_registers.unset(index); |
| 81 | self.markRegIndexUsed(indexOfRegIntoTracked(reg) orelse return); |
| 77 | 82 | } |
| 78 | 83 | |
| 84 | fn markRegIndexFree(self: *Self, tracked_index: TrackedIndex) void { |
| 85 | self.free_registers.set(tracked_index); |
| 86 | } |
| 79 | 87 | fn markRegFree(self: *Self, reg: Register) void { |
| 80 | | const index = indexOfRegIntoTracked(reg) orelse return; |
| 81 | | self.free_registers.set(index); |
| 88 | self.markRegIndexFree(indexOfRegIntoTracked(reg) orelse return); |
| 82 | 89 | } |
| 83 | 90 | |
| 84 | 91 | pub fn indexOfReg( |
| 85 | | comptime registers: []const Register, |
| 92 | comptime set: []const Register, |
| 86 | 93 | reg: Register, |
| 87 | | ) ?std.math.IntFittingRange(0, registers.len - 1) { |
| 88 | | inline for (tracked_registers, 0..) |cpreg, i| { |
| 89 | | if (reg.id() == cpreg.id()) return i; |
| 94 | ) ?std.math.IntFittingRange(0, set.len - 1) { |
| 95 | const Id = @TypeOf(reg.id()); |
| 96 | comptime var min_id: Id = std.math.maxInt(Id); |
| 97 | comptime var max_id: Id = std.math.minInt(Id); |
| 98 | inline for (set) |elem| { |
| 99 | const elem_id = comptime elem.id(); |
| 100 | min_id = @min(elem_id, min_id); |
| 101 | max_id = @max(elem_id, max_id); |
| 90 | 102 | } |
| 91 | | return null; |
| 103 | |
| 104 | const OptionalIndex = std.math.IntFittingRange(0, set.len); |
| 105 | comptime var map = [1]OptionalIndex{set.len} ** (max_id + 1 - min_id); |
| 106 | inline for (set, 0..) |elem, elem_index| map[comptime elem.id() - min_id] = elem_index; |
| 107 | |
| 108 | const id_index = reg.id() -% min_id; |
| 109 | if (id_index >= map.len) return null; |
| 110 | const set_index = map[id_index]; |
| 111 | return if (set_index < set.len) @intCast(set_index) else null; |
| 92 | 112 | } |
| 93 | 113 | |
| 94 | | pub fn indexOfRegIntoTracked( |
| 95 | | reg: Register, |
| 96 | | ) ?std.math.IntFittingRange(0, tracked_registers.len) { |
| 114 | pub fn indexOfRegIntoTracked(reg: Register) ?TrackedIndex { |
| 97 | 115 | return indexOfReg(tracked_registers, reg); |
| 98 | 116 | } |
| 99 | 117 | |
| 100 | | pub fn regAtTrackedIndex(index: std.math.IntFittingRange(0, tracked_registers.len)) Register { |
| 101 | | return tracked_registers[index]; |
| 118 | pub fn regAtTrackedIndex(tracked_index: TrackedIndex) Register { |
| 119 | return tracked_registers[tracked_index]; |
| 102 | 120 | } |
| 103 | 121 | |
| 104 | 122 | /// Returns true when this register is not tracked |
| 123 | pub fn isRegIndexFree(self: Self, tracked_index: TrackedIndex) bool { |
| 124 | return self.free_registers.isSet(tracked_index); |
| 125 | } |
| 105 | 126 | pub fn isRegFree(self: Self, reg: Register) bool { |
| 106 | | const index = indexOfRegIntoTracked(reg) orelse return true; |
| 107 | | return self.free_registers.isSet(index); |
| 127 | return self.isRegIndexFree(indexOfRegIntoTracked(reg) orelse return true); |
| 108 | 128 | } |
| 109 | 129 | |
| 110 | 130 | /// Returns whether this register was allocated in the course |
| ... | ... | @@ -119,14 +139,14 @@ pub fn RegisterManager( |
| 119 | 139 | /// Returns whether this register is locked |
| 120 | 140 | /// |
| 121 | 141 | /// Returns false when this register is not tracked |
| 142 | fn isRegIndexLocked(self: Self, tracked_index: TrackedIndex) bool { |
| 143 | return self.locked_registers.isSet(tracked_index); |
| 144 | } |
| 122 | 145 | pub fn isRegLocked(self: Self, reg: Register) bool { |
| 123 | | const index = indexOfRegIntoTracked(reg) orelse return false; |
| 124 | | return self.locked_registers.isSet(index); |
| 146 | return self.isRegIndexLocked(indexOfRegIntoTracked(reg) orelse return false); |
| 125 | 147 | } |
| 126 | 148 | |
| 127 | | pub const RegisterLock = struct { |
| 128 | | register: Register, |
| 129 | | }; |
| 149 | pub const RegisterLock = struct { tracked_index: TrackedIndex }; |
| 130 | 150 | |
| 131 | 151 | /// Prevents the register from being allocated until they are |
| 132 | 152 | /// unlocked again. |
| ... | ... | @@ -134,25 +154,29 @@ pub fn RegisterManager( |
| 134 | 154 | /// locked, or `null` otherwise. |
| 135 | 155 | /// Only the owner of the `RegisterLock` can unlock the |
| 136 | 156 | /// register later. |
| 137 | | pub fn lockReg(self: *Self, reg: Register) ?RegisterLock { |
| 138 | | log.debug("locking {}", .{reg}); |
| 139 | | if (self.isRegLocked(reg)) { |
| 157 | pub fn lockRegIndex(self: *Self, tracked_index: TrackedIndex) ?RegisterLock { |
| 158 | log.debug("locking {}", .{regAtTrackedIndex(tracked_index)}); |
| 159 | if (self.isRegIndexLocked(tracked_index)) { |
| 140 | 160 | log.debug(" register already locked", .{}); |
| 141 | 161 | return null; |
| 142 | 162 | } |
| 143 | | const index = indexOfRegIntoTracked(reg) orelse return null; |
| 144 | | self.locked_registers.set(index); |
| 145 | | return RegisterLock{ .register = reg }; |
| 163 | self.locked_registers.set(tracked_index); |
| 164 | return RegisterLock{ .tracked_index = tracked_index }; |
| 165 | } |
| 166 | pub fn lockReg(self: *Self, reg: Register) ?RegisterLock { |
| 167 | return self.lockRegIndex(indexOfRegIntoTracked(reg) orelse return null); |
| 146 | 168 | } |
| 147 | 169 | |
| 148 | 170 | /// Like `lockReg` but asserts the register was unused always |
| 149 | 171 | /// returning a valid lock. |
| 172 | pub fn lockRegIndexAssumeUnused(self: *Self, tracked_index: TrackedIndex) RegisterLock { |
| 173 | log.debug("locking asserting free {}", .{regAtTrackedIndex(tracked_index)}); |
| 174 | assert(!self.isRegIndexLocked(tracked_index)); |
| 175 | self.locked_registers.set(tracked_index); |
| 176 | return RegisterLock{ .tracked_index = tracked_index }; |
| 177 | } |
| 150 | 178 | pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock { |
| 151 | | log.debug("locking asserting free {}", .{reg}); |
| 152 | | assert(!self.isRegLocked(reg)); |
| 153 | | const index = indexOfRegIntoTracked(reg) orelse unreachable; |
| 154 | | self.locked_registers.set(index); |
| 155 | | return RegisterLock{ .register = reg }; |
| 179 | return self.lockRegIndexAssumeUnused(indexOfRegIntoTracked(reg) orelse unreachable); |
| 156 | 180 | } |
| 157 | 181 | |
| 158 | 182 | /// Like `lockReg` but locks multiple registers. |
| ... | ... | @@ -181,9 +205,8 @@ pub fn RegisterManager( |
| 181 | 205 | /// Requires `RegisterLock` to unlock a register. |
| 182 | 206 | /// Call `lockReg` to obtain the lock first. |
| 183 | 207 | pub fn unlockReg(self: *Self, lock: RegisterLock) void { |
| 184 | | log.debug("unlocking {}", .{lock.register}); |
| 185 | | const index = indexOfRegIntoTracked(lock.register) orelse return; |
| 186 | | self.locked_registers.unset(index); |
| 208 | log.debug("unlocking {}", .{regAtTrackedIndex(lock.tracked_index)}); |
| 209 | self.locked_registers.unset(lock.tracked_index); |
| 187 | 210 | } |
| 188 | 211 | |
| 189 | 212 | /// Returns true when at least one register is locked |
| ... | ... | @@ -319,44 +342,63 @@ pub fn RegisterManager( |
| 319 | 342 | /// Spills the register if it is currently allocated. If a |
| 320 | 343 | /// corresponding instruction is passed, will also track this |
| 321 | 344 | /// register. |
| 322 | | pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void { |
| 323 | | const index = indexOfRegIntoTracked(reg) orelse return; |
| 324 | | log.debug("getReg {} for inst {?}", .{ reg, inst }); |
| 325 | | |
| 326 | | if (!self.isRegFree(reg)) { |
| 327 | | self.markRegAllocated(reg); |
| 345 | fn getRegIndex( |
| 346 | self: *Self, |
| 347 | tracked_index: TrackedIndex, |
| 348 | inst: ?Air.Inst.Index, |
| 349 | ) AllocateRegistersError!void { |
| 350 | log.debug("getReg {} for inst {?}", .{ regAtTrackedIndex(tracked_index), inst }); |
| 351 | if (!self.isRegIndexFree(tracked_index)) { |
| 352 | self.markRegIndexAllocated(tracked_index); |
| 328 | 353 | |
| 329 | 354 | // Move the instruction that was previously there to a |
| 330 | 355 | // stack allocation. |
| 331 | | const spilled_inst = self.registers[index]; |
| 332 | | if (inst) |tracked_inst| self.registers[index] = tracked_inst; |
| 333 | | try self.getFunction().spillInstruction(reg, spilled_inst); |
| 334 | | if (inst == null) self.freeReg(reg); |
| 335 | | } else self.getRegAssumeFree(reg, inst); |
| 356 | const spilled_inst = self.registers[tracked_index]; |
| 357 | if (inst) |tracked_inst| self.registers[tracked_index] = tracked_inst; |
| 358 | try self.getFunction().spillInstruction(regAtTrackedIndex(tracked_index), spilled_inst); |
| 359 | if (inst == null) self.freeRegIndex(tracked_index); |
| 360 | } else self.getRegIndexAssumeFree(tracked_index, inst); |
| 361 | } |
| 362 | pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void { |
| 363 | return self.getRegIndex(indexOfRegIntoTracked(reg) orelse return, inst); |
| 364 | } |
| 365 | pub fn getKnownReg( |
| 366 | self: *Self, |
| 367 | comptime reg: Register, |
| 368 | inst: ?Air.Inst.Index, |
| 369 | ) AllocateRegistersError!void { |
| 370 | return self.getRegIndex((comptime indexOfRegIntoTracked(reg)) orelse return, inst); |
| 336 | 371 | } |
| 337 | 372 | |
| 338 | 373 | /// Allocates the specified register with the specified |
| 339 | 374 | /// instruction. Asserts that the register is free and no |
| 340 | 375 | /// spilling is necessary. |
| 341 | | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: ?Air.Inst.Index) void { |
| 342 | | const index = indexOfRegIntoTracked(reg) orelse return; |
| 343 | | log.debug("getRegAssumeFree {} for inst {?}", .{ reg, inst }); |
| 344 | | self.markRegAllocated(reg); |
| 376 | fn getRegIndexAssumeFree( |
| 377 | self: *Self, |
| 378 | tracked_index: TrackedIndex, |
| 379 | inst: ?Air.Inst.Index, |
| 380 | ) void { |
| 381 | log.debug("getRegAssumeFree {} for inst {?}", .{ regAtTrackedIndex(tracked_index), inst }); |
| 382 | self.markRegIndexAllocated(tracked_index); |
| 345 | 383 | |
| 346 | | assert(self.isRegFree(reg)); |
| 384 | assert(self.isRegIndexFree(tracked_index)); |
| 347 | 385 | if (inst) |tracked_inst| { |
| 348 | | self.registers[index] = tracked_inst; |
| 349 | | self.markRegUsed(reg); |
| 386 | self.registers[tracked_index] = tracked_inst; |
| 387 | self.markRegIndexUsed(tracked_index); |
| 350 | 388 | } |
| 351 | 389 | } |
| 390 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: ?Air.Inst.Index) void { |
| 391 | self.getRegIndexAssumeFree(indexOfRegIntoTracked(reg) orelse return, inst); |
| 392 | } |
| 352 | 393 | |
| 353 | 394 | /// Marks the specified register as free |
| 395 | fn freeRegIndex(self: *Self, tracked_index: TrackedIndex) void { |
| 396 | log.debug("freeing register {}", .{regAtTrackedIndex(tracked_index)}); |
| 397 | self.registers[tracked_index] = undefined; |
| 398 | self.markRegIndexFree(tracked_index); |
| 399 | } |
| 354 | 400 | pub fn freeReg(self: *Self, reg: Register) void { |
| 355 | | const index = indexOfRegIntoTracked(reg) orelse return; |
| 356 | | log.debug("freeing register {}", .{reg}); |
| 357 | | |
| 358 | | self.registers[index] = undefined; |
| 359 | | self.markRegFree(reg); |
| 401 | self.freeRegIndex(indexOfRegIntoTracked(reg) orelse return); |
| 360 | 402 | } |
| 361 | 403 | }; |
| 362 | 404 | } |