authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-19 15:34:13+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-19 19:39:34+02:00
log283f40e4e9c44986353ba8abcc760684e9adf6cc
tree1c46fba4df6ea88b7d375b26c54058cc088aa74f
parent080d138b9d06402a8fbb70b4addc90751c6b798e

x64: use StaticBitSet instead of an integer internally in RegisterManager


3 files changed, 78 insertions(+), 68 deletions(-)

src/arch/x86_64/CodeGen.zig+5-6
...@@ -21,7 +21,6 @@ const Emit = @import("Emit.zig");...@@ -21,7 +21,6 @@ const Emit = @import("Emit.zig");
21const Liveness = @import("../../Liveness.zig");21const Liveness = @import("../../Liveness.zig");
22const Mir = @import("Mir.zig");22const Mir = @import("Mir.zig");
23const Module = @import("../../Module.zig");23const Module = @import("../../Module.zig");
24const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
25const Target = std.Target;24const Target = std.Target;
26const Type = @import("../../type.zig").Type;25const Type = @import("../../type.zig").Type;
27const TypedValue = @import("../../TypedValue.zig");26const TypedValue = @import("../../TypedValue.zig");
...@@ -32,15 +31,15 @@ const abi = @import("abi.zig");...@@ -32,15 +31,15 @@ const abi = @import("abi.zig");
3231
33const callee_preserved_regs = abi.callee_preserved_regs;32const callee_preserved_regs = abi.callee_preserved_regs;
34const caller_preserved_regs = abi.caller_preserved_regs;33const caller_preserved_regs = abi.caller_preserved_regs;
35const allocatable_registers = abi.allocatable_registers;
36const c_abi_int_param_regs = abi.c_abi_int_param_regs;34const c_abi_int_param_regs = abi.c_abi_int_param_regs;
37const c_abi_int_return_regs = abi.c_abi_int_return_regs;35const c_abi_int_return_regs = abi.c_abi_int_return_regs;
38const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers);36
37const RegisterManager = abi.RegisterManager;
39const RegisterLock = RegisterManager.RegisterLock;38const RegisterLock = RegisterManager.RegisterLock;
40const Register = bits.Register;39const Register = bits.Register;
41const RegisterClass = abi.RegisterClass;40
42const gp = RegisterClass.gp;41const gp = abi.RegisterClass.gp;
43const avx = RegisterClass.avx;42const avx = abi.RegisterClass.avx;
4443
45const InnerError = error{44const InnerError = error{
46 OutOfMemory,45 OutOfMemory,
src/arch/x86_64/abi.zig+26-9
...@@ -3,6 +3,7 @@ const Type = @import("../../type.zig").Type;...@@ -3,6 +3,7 @@ const Type = @import("../../type.zig").Type;
3const Target = std.Target;3const Target = std.Target;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const Register = @import("bits.zig").Register;5const Register = @import("bits.zig").Register;
6const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
67
7pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none };8pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none };
89
...@@ -378,18 +379,34 @@ pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };...@@ -378,18 +379,34 @@ pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };
378/// the caller relinquishes control to a subroutine via call instruction (or similar).379/// the caller relinquishes control to a subroutine via call instruction (or similar).
379/// In other words, these registers are free to use by the callee.380/// In other words, these registers are free to use by the callee.
380pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };381pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
381pub const avx_regs = [_]Register{
382 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
383 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,
384};
385pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ avx_regs;
386382
387pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };383pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
388pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };384pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
389385
390// Masks for register manager386const avx_regs = [_]Register{
391const FreeRegInt = std.meta.Int(.unsigned, allocatable_registers.len);387 .ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
388 .ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,
389};
390const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ avx_regs;
391pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
392
393// Register classes
394const RegisterBitSet = RegisterManager.RegisterBitSet;
392pub const RegisterClass = struct {395pub const RegisterClass = struct {
393 pub const gp: FreeRegInt = 0x3fff;396 pub const gp: RegisterBitSet = blk: {
394 pub const avx: FreeRegInt = 0x3fff_c000;397 var set = RegisterBitSet.initEmpty();
398 set.setRangeValue(.{
399 .start = 0,
400 .end = caller_preserved_regs.len + callee_preserved_regs.len,
401 }, true);
402 break :blk set;
403 };
404 pub const avx: RegisterBitSet = blk: {
405 var set = RegisterBitSet.initEmpty();
406 set.setRangeValue(.{
407 .start = caller_preserved_regs.len + callee_preserved_regs.len,
408 .end = allocatable_registers.len,
409 }, true);
410 break :blk set;
411 };
395};412};
src/register_manager.zig+47-53
...@@ -4,6 +4,7 @@ const mem = std.mem;...@@ -4,6 +4,7 @@ const mem = std.mem;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const Air = @import("Air.zig");6const Air = @import("Air.zig");
7const StaticBitSet = std.bit_set.StaticBitSet;
7const Type = @import("type.zig").Type;8const Type = @import("type.zig").Type;
8const Module = @import("Module.zig");9const Module = @import("Module.zig");
9const expect = std.testing.expect;10const expect = std.testing.expect;
...@@ -41,66 +42,54 @@ pub fn RegisterManager(...@@ -41,66 +42,54 @@ pub fn RegisterManager(
41 registers: [tracked_registers.len]Air.Inst.Index = undefined,42 registers: [tracked_registers.len]Air.Inst.Index = undefined,
42 /// Tracks which registers are free (in which case the43 /// Tracks which registers are free (in which case the
43 /// corresponding bit is set to 1)44 /// corresponding bit is set to 1)
44 free_registers: FreeRegInt = math.maxInt(FreeRegInt),45 free_registers: RegisterBitSet = RegisterBitSet.initFull(),
45 /// Tracks all registers allocated in the course of this46 /// Tracks all registers allocated in the course of this
46 /// function47 /// function
47 allocated_registers: FreeRegInt = 0,48 allocated_registers: RegisterBitSet = RegisterBitSet.initEmpty(),
48 /// Tracks registers which are locked from being allocated49 /// Tracks registers which are locked from being allocated
49 locked_registers: FreeRegInt = 0,50 locked_registers: RegisterBitSet = RegisterBitSet.initEmpty(),
5051
51 const Self = @This();52 const Self = @This();
5253
53 /// An integer whose bits represent all the registers and54 pub const RegisterBitSet = StaticBitSet(tracked_registers.len);
54 /// whether they are free.
55 const FreeRegInt = std.meta.Int(.unsigned, tracked_registers.len);
56 const ShiftInt = math.Log2Int(FreeRegInt);
5755
58 fn getFunction(self: *Self) *Function {56 fn getFunction(self: *Self) *Function {
59 return @fieldParentPtr(Function, "register_manager", self);57 return @fieldParentPtr(Function, "register_manager", self);
60 }58 }
6159
62 fn getRegisterMask(reg: Register) ?FreeRegInt {
63 const index = indexOfRegIntoTracked(reg) orelse return null;
64 const shift = @intCast(ShiftInt, index);
65 const mask = @as(FreeRegInt, 1) << shift;
66 return mask;
67 }
68
69 fn excludeRegister(reg: Register, mask: FreeRegInt) bool {
70 const reg_mask = getRegisterMask(reg) orelse return true;
71 return reg_mask & mask == 0;
72 }
73
74 fn markRegAllocated(self: *Self, reg: Register) void {60 fn markRegAllocated(self: *Self, reg: Register) void {
75 const mask = getRegisterMask(reg) orelse return;61 const index = indexOfRegIntoTracked(reg) orelse return;
76 self.allocated_registers |= mask;62 self.allocated_registers.set(index);
77 }63 }
7864
79 fn markRegUsed(self: *Self, reg: Register) void {65 fn markRegUsed(self: *Self, reg: Register) void {
80 const mask = getRegisterMask(reg) orelse return;66 const index = indexOfRegIntoTracked(reg) orelse return;
81 self.free_registers &= ~mask;67 self.free_registers.unset(index);
82 }68 }
8369
84 fn markRegFree(self: *Self, reg: Register) void {70 fn markRegFree(self: *Self, reg: Register) void {
85 const mask = getRegisterMask(reg) orelse return;71 const index = indexOfRegIntoTracked(reg) orelse return;
86 self.free_registers |= mask;72 self.free_registers.set(index);
87 }73 }
8874
89 pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) {75 pub fn indexOfReg(
76 comptime registers: []const Register,
77 reg: Register,
78 ) ?std.math.IntFittingRange(0, registers.len - 1) {
90 inline for (tracked_registers) |cpreg, i| {79 inline for (tracked_registers) |cpreg, i| {
91 if (reg.id() == cpreg.id()) return i;80 if (reg.id() == cpreg.id()) return i;
92 }81 }
93 return null;82 return null;
94 }83 }
9584
96 pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt {85 pub fn indexOfRegIntoTracked(reg: Register) ?RegisterBitSet.ShiftInt {
97 return indexOfReg(tracked_registers, reg);86 return indexOfReg(tracked_registers, reg);
98 }87 }
9988
100 /// Returns true when this register is not tracked89 /// Returns true when this register is not tracked
101 pub fn isRegFree(self: Self, reg: Register) bool {90 pub fn isRegFree(self: Self, reg: Register) bool {
102 const mask = getRegisterMask(reg) orelse return true;91 const index = indexOfRegIntoTracked(reg) orelse return true;
103 return self.free_registers & mask != 0;92 return self.free_registers.isSet(index);
104 }93 }
10594
106 /// Returns whether this register was allocated in the course95 /// Returns whether this register was allocated in the course
...@@ -108,16 +97,16 @@ pub fn RegisterManager(...@@ -108,16 +97,16 @@ pub fn RegisterManager(
108 ///97 ///
109 /// Returns false when this register is not tracked98 /// Returns false when this register is not tracked
110 pub fn isRegAllocated(self: Self, reg: Register) bool {99 pub fn isRegAllocated(self: Self, reg: Register) bool {
111 const mask = getRegisterMask(reg) orelse return false;100 const index = indexOfRegIntoTracked(reg) orelse return false;
112 return self.allocated_registers & mask != 0;101 return self.allocated_registers.isSet(index);
113 }102 }
114103
115 /// Returns whether this register is locked104 /// Returns whether this register is locked
116 ///105 ///
117 /// Returns false when this register is not tracked106 /// Returns false when this register is not tracked
118 pub fn isRegLocked(self: Self, reg: Register) bool {107 pub fn isRegLocked(self: Self, reg: Register) bool {
119 const mask = getRegisterMask(reg) orelse return false;108 const index = indexOfRegIntoTracked(reg) orelse return false;
120 return self.locked_registers & mask != 0;109 return self.locked_registers.isSet(index);
121 }110 }
122111
123 pub const RegisterLock = struct {112 pub const RegisterLock = struct {
...@@ -136,8 +125,8 @@ pub fn RegisterManager(...@@ -136,8 +125,8 @@ pub fn RegisterManager(
136 log.debug(" register already locked", .{});125 log.debug(" register already locked", .{});
137 return null;126 return null;
138 }127 }
139 const mask = getRegisterMask(reg) orelse return null;128 const index = indexOfRegIntoTracked(reg) orelse return null;
140 self.locked_registers |= mask;129 self.locked_registers.set(index);
141 return RegisterLock{ .register = reg };130 return RegisterLock{ .register = reg };
142 }131 }
143132
...@@ -146,8 +135,8 @@ pub fn RegisterManager(...@@ -146,8 +135,8 @@ pub fn RegisterManager(
146 pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock {135 pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock {
147 log.debug("locking asserting free {}", .{reg});136 log.debug("locking asserting free {}", .{reg});
148 assert(!self.isRegLocked(reg));137 assert(!self.isRegLocked(reg));
149 const mask = getRegisterMask(reg) orelse unreachable;138 const index = indexOfRegIntoTracked(reg) orelse unreachable;
150 self.locked_registers |= mask;139 self.locked_registers.set(index);
151 return RegisterLock{ .register = reg };140 return RegisterLock{ .register = reg };
152 }141 }
153142
...@@ -169,17 +158,17 @@ pub fn RegisterManager(...@@ -169,17 +158,17 @@ pub fn RegisterManager(
169 /// Call `lockReg` to obtain the lock first.158 /// Call `lockReg` to obtain the lock first.
170 pub fn unlockReg(self: *Self, lock: RegisterLock) void {159 pub fn unlockReg(self: *Self, lock: RegisterLock) void {
171 log.debug("unlocking {}", .{lock.register});160 log.debug("unlocking {}", .{lock.register});
172 const mask = getRegisterMask(lock.register) orelse return;161 const index = indexOfRegIntoTracked(lock.register) orelse return;
173 self.locked_registers &= ~mask;162 self.locked_registers.unset(index);
174 }163 }
175164
176 /// Returns true when at least one register is locked165 /// Returns true when at least one register is locked
177 pub fn lockedRegsExist(self: Self) bool {166 pub fn lockedRegsExist(self: Self) bool {
178 return self.locked_registers != 0;167 return self.locked_registers.count() > 0;
179 }168 }
180169
181 const AllocOpts = struct {170 const AllocOpts = struct {
182 selector_mask: ?FreeRegInt = null,171 selector_mask: ?RegisterBitSet = null,
183 };172 };
184173
185 /// Allocates a specified number of registers, optionally174 /// Allocates a specified number of registers, optionally
...@@ -193,17 +182,22 @@ pub fn RegisterManager(...@@ -193,17 +182,22 @@ pub fn RegisterManager(
193 ) ?[count]Register {182 ) ?[count]Register {
194 comptime assert(count > 0 and count <= tracked_registers.len);183 comptime assert(count > 0 and count <= tracked_registers.len);
195184
196 const selector_mask = if (opts.selector_mask) |mask| mask else ~@as(FreeRegInt, 0);185 const available_registers = opts.selector_mask orelse RegisterBitSet.initFull();
197 const free_registers = self.free_registers & selector_mask;186
198 const free_and_not_locked_registers = free_registers & ~self.locked_registers;187 var free_and_not_locked_registers = self.free_registers;
199 const free_and_not_locked_registers_count = @popCount(FreeRegInt, free_and_not_locked_registers);188 free_and_not_locked_registers.setIntersection(available_registers);
200 if (free_and_not_locked_registers_count < count) return null;189
190 var unlocked_registers = self.locked_registers;
191 unlocked_registers.toggleAll();
192
193 free_and_not_locked_registers.setIntersection(unlocked_registers);
194
195 if (free_and_not_locked_registers.count() < count) return null;
201196
202 var regs: [count]Register = undefined;197 var regs: [count]Register = undefined;
203 var i: usize = 0;198 var i: usize = 0;
204 for (tracked_registers) |reg| {199 for (tracked_registers) |reg| {
205 if (i >= count) break;200 if (i >= count) break;
206 if (excludeRegister(reg, selector_mask)) continue;
207 if (self.isRegLocked(reg)) continue;201 if (self.isRegLocked(reg)) continue;
208 if (!self.isRegFree(reg)) continue;202 if (!self.isRegFree(reg)) continue;
209203
...@@ -244,11 +238,12 @@ pub fn RegisterManager(...@@ -244,11 +238,12 @@ pub fn RegisterManager(
244 ) AllocateRegistersError![count]Register {238 ) AllocateRegistersError![count]Register {
245 comptime assert(count > 0 and count <= tracked_registers.len);239 comptime assert(count > 0 and count <= tracked_registers.len);
246240
247 const selector_mask = if (opts.selector_mask) |mask| mask else ~@as(FreeRegInt, 0);241 const available_registers = opts.selector_mask orelse RegisterBitSet.initFull();
248 const available_registers_count = @popCount(FreeRegInt, selector_mask);242
249 const locked_registers = self.locked_registers & selector_mask;243 var locked_registers = self.locked_registers;
250 const locked_registers_count = @popCount(FreeRegInt, locked_registers);244 locked_registers.setIntersection(available_registers);
251 if (count > available_registers_count - locked_registers_count) return error.OutOfRegisters;245
246 if (count > available_registers.count() - locked_registers.count()) return error.OutOfRegisters;
252247
253 const result = self.tryAllocRegs(count, insts, opts) orelse blk: {248 const result = self.tryAllocRegs(count, insts, opts) orelse blk: {
254 // We'll take over the first count registers. Spill249 // We'll take over the first count registers. Spill
...@@ -258,7 +253,6 @@ pub fn RegisterManager(...@@ -258,7 +253,6 @@ pub fn RegisterManager(
258 var i: usize = 0;253 var i: usize = 0;
259 for (tracked_registers) |reg| {254 for (tracked_registers) |reg| {
260 if (i >= count) break;255 if (i >= count) break;
261 if (excludeRegister(reg, selector_mask)) continue;
262 if (self.isRegLocked(reg)) continue;256 if (self.isRegLocked(reg)) continue;
263257
264 regs[i] = reg;258 regs[i] = reg;