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