authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-04-18 20:31:14+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-25 13:20:53-04:00
log3a55cda14d38a2f83e3c6a9ecb782fcb81d1347d
tree7a6654cb77299600cf6039f5a306252c6a3d89fb
parent37b05742ff1544bccf7c8ae9b12c6707a5a54df2

stage2 register manager: Use an array instead of a hashmap for tracking

allocated registers

2 files changed, 72 insertions(+), 67 deletions(-)

src/codegen.zig+29-17
......@@ -449,7 +449,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
449449 .rbrace_src = src_data.rbrace_src,
450450 .source = src_data.source,
451451 };
452 defer function.register_manager.deinit(bin_file.allocator);
453452 defer function.stack.deinit(bin_file.allocator);
454453 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
455454
......@@ -779,8 +778,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
779778 branch.inst_table.putAssumeCapacity(inst, .dead);
780779 switch (prev_value) {
781780 .register => |reg| {
782 const canon_reg = toCanonicalReg(reg);
783 self.register_manager.freeReg(canon_reg);
781 // TODO separate architectures with registers from
782 // stack-based architectures (spu_2)
783 if (callee_preserved_regs.len > 0) {
784 const canon_reg = toCanonicalReg(reg);
785 self.register_manager.freeReg(canon_reg);
786 }
784787 },
785788 else => {}, // TODO process stack allocation death
786789 }
......@@ -920,9 +923,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
920923 const ptr_bits = arch.ptrBitWidth();
921924 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
922925 if (abi_size <= ptr_bytes) {
923 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
924 if (self.register_manager.tryAllocReg(inst)) |reg| {
925 return MCValue{ .register = registerAlias(reg, abi_size) };
926 // TODO separate architectures with registers from
927 // stack-based architectures (spu_2)
928 if (callee_preserved_regs.len > 0) {
929 if (self.register_manager.tryAllocReg(inst)) |reg| {
930 return MCValue{ .register = registerAlias(reg, abi_size) };
931 }
926932 }
927933 }
928934 }
......@@ -952,8 +958,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
952958 /// `reg_owner` is the instruction that gets associated with the register in the register table.
953959 /// This can have a side effect of spilling instructions to the stack to free up a register.
954960 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
955 try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1));
956
957961 const reg = try self.register_manager.allocReg(reg_owner);
958962 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
959963 return MCValue{ .register = reg };
......@@ -1240,10 +1244,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12401244 .register => |reg| {
12411245 // If it's in the registers table, need to associate the register with the
12421246 // new instruction.
1243 if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| {
1244 entry.value = inst;
1247 // TODO separate architectures with registers from
1248 // stack-based architectures (spu_2)
1249 if (callee_preserved_regs.len > 0) {
1250 if (reg.allocIndex()) |index| {
1251 if (!self.register_manager.isRegFree(reg)) {
1252 self.register_manager.registers[index] = inst;
1253 }
1254 }
1255 log.debug("reusing {} => {*}", .{ reg, inst });
12451256 }
1246 log.debug("reusing {} => {*}", .{ reg, inst });
12471257 },
12481258 .stack_offset => |off| {
12491259 log.debug("reusing stack offset {} => {*}", .{ off, inst });
......@@ -1738,6 +1748,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17381748 const arg_index = self.arg_index;
17391749 self.arg_index += 1;
17401750
1751 // TODO separate architectures with registers from
1752 // stack-based architectures (spu_2)
17411753 if (callee_preserved_regs.len == 0) {
17421754 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
17431755 }
......@@ -1769,7 +1781,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17691781
17701782 switch (mcv) {
17711783 .register => |reg| {
1772 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
17731784 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
17741785 },
17751786 else => {},
......@@ -2075,7 +2086,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20752086 switch (mc_arg) {
20762087 .none => continue,
20772088 .register => |reg| {
2078 try self.register_manager.getRegWithoutTracking(reg);
2089 // TODO prevent this macho if block to be generated for all archs
2090 switch (arch) {
2091 .x86_64, .aarch64 => try self.register_manager.getRegWithoutTracking(reg),
2092 else => unreachable,
2093 }
20792094 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
20802095 },
20812096 .stack_offset => {
......@@ -2397,8 +2412,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
23972412 const parent_free_registers = self.register_manager.free_registers;
23982413 var parent_stack = try self.stack.clone(self.gpa);
23992414 defer parent_stack.deinit(self.gpa);
2400 var parent_registers = try self.register_manager.registers.clone(self.gpa);
2401 defer parent_registers.deinit(self.gpa);
2415 const parent_registers = self.register_manager.registers;
24022416
24032417 try self.branch_stack.append(.{});
24042418
......@@ -2414,9 +2428,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24142428 var saved_then_branch = self.branch_stack.pop();
24152429 defer saved_then_branch.deinit(self.gpa);
24162430
2417 self.register_manager.registers.deinit(self.gpa);
24182431 self.register_manager.registers = parent_registers;
2419 parent_registers = .{};
24202432
24212433 self.stack.deinit(self.gpa);
24222434 self.stack = parent_stack;
src/register_manager.zig+43-50
......@@ -16,7 +16,7 @@ pub fn RegisterManager(
1616) type {
1717 return struct {
1818 /// The key must be canonical register.
19 registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
19 registers: [callee_preserved_regs.len]?*ir.Inst = [_]?*ir.Inst{null} ** callee_preserved_regs.len,
2020 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
2121 /// Tracks all registers allocated in the course of this function
2222 allocated_registers: FreeRegInt = 0,
......@@ -31,14 +31,6 @@ pub fn RegisterManager(
3131 return @fieldParentPtr(Function, "register_manager", self);
3232 }
3333
34 pub fn deinit(self: *Self, allocator: *Allocator) void {
35 self.registers.deinit(allocator);
36 }
37
38 fn isTracked(reg: Register) bool {
39 return reg.allocIndex() != null;
40 }
41
4234 fn markRegUsed(self: *Self, reg: Register) void {
4335 if (FreeRegInt == u0) return;
4436 const index = reg.allocIndex() orelse return;
......@@ -73,13 +65,13 @@ pub fn RegisterManager(
7365 return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;
7466 }
7567
76 /// Before calling, must ensureCapacity + count on self.registers.
7768 /// Returns `null` if all registers are allocated.
7869 pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register {
7970 if (self.tryAllocRegsWithoutTracking(count)) |regs| {
8071 for (regs) |reg, i| {
72 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
73 self.registers[index] = insts[i];
8174 self.markRegUsed(reg);
82 self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
8375 }
8476
8577 return regs;
......@@ -88,13 +80,11 @@ pub fn RegisterManager(
8880 }
8981 }
9082
91 /// Before calling, must ensureCapacity + 1 on self.registers.
9283 /// Returns `null` if all registers are allocated.
9384 pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
9485 return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null;
9586 }
9687
97 /// Before calling, must ensureCapacity + count on self.registers.
9888 pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register {
9989 comptime assert(count > 0 and count <= callee_preserved_regs.len);
10090
......@@ -106,24 +96,22 @@ pub fn RegisterManager(
10696 std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);
10797
10898 for (regs) |reg, i| {
99 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
109100 if (self.isRegFree(reg)) {
110101 self.markRegUsed(reg);
111 self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
112102 } else {
113 const regs_entry = self.registers.getEntry(reg).?;
114 const spilled_inst = regs_entry.value;
115 regs_entry.value = insts[i];
103 const spilled_inst = self.registers[index].?;
116104 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
117105 }
106 self.registers[index] = insts[i];
118107 }
119108
120109 break :blk regs;
121110 };
122111 }
123112
124 /// Before calling, must ensureCapacity + 1 on self.registers.
125113 pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {
126 return (try allocRegs(self, 1, .{inst}))[0];
114 return (try self.allocRegs(1, .{inst}))[0];
127115 }
128116
129117 /// Does not track the registers.
......@@ -150,37 +138,48 @@ pub fn RegisterManager(
150138 /// Does not track the register.
151139 /// Returns `null` if all registers are allocated.
152140 pub fn tryAllocRegWithoutTracking(self: *Self) ?Register {
153 return if (tryAllocRegsWithoutTracking(self, 1)) |regs| regs[0] else null;
141 return if (self.tryAllocRegsWithoutTracking(1)) |regs| regs[0] else null;
154142 }
155143
156 /// Does not track the register.
157 pub fn allocRegWithoutTracking(self: *Self) !Register {
158 return self.tryAllocRegWithoutTracking() orelse b: {
159 // We'll take over the first register. Move the instruction that was previously
160 // there to a stack allocation.
161 const reg = callee_preserved_regs[0];
162 const regs_entry = self.registers.remove(reg).?;
163 const spilled_inst = regs_entry.value;
164 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
165 self.markRegFree(reg);
144 /// Does not track the registers
145 pub fn allocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ![count]Register {
146 return self.tryAllocRegsWithoutTracking(count) orelse blk: {
147 // We'll take over the first count registers. Spill
148 // the instructions that were previously there to a
149 // stack allocations.
150 var regs: [count]Register = undefined;
151 std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);
166152
167 break :b reg;
153 for (regs) |reg, i| {
154 const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null
155 if (!self.isRegFree(reg)) {
156 const spilled_inst = self.registers[index].?;
157 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
158 self.registers[index] = null;
159 self.markRegFree(reg);
160 }
161 }
162
163 break :blk regs;
168164 };
169165 }
170166
167 /// Does not track the register.
168 pub fn allocRegWithoutTracking(self: *Self) !Register {
169 return (try self.allocRegsWithoutTracking(1))[0];
170 }
171
171172 /// Allocates the specified register with the specified
172173 /// instruction. Spills the register if it is currently
173174 /// allocated.
174 /// Before calling, must ensureCapacity + 1 on self.registers.
175175 pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void {
176 if (!isTracked(reg)) return;
176 const index = reg.allocIndex() orelse return;
177177
178178 if (!self.isRegFree(reg)) {
179179 // Move the instruction that was previously there to a
180180 // stack allocation.
181 const regs_entry = self.registers.getEntry(reg).?;
182 const spilled_inst = regs_entry.value;
183 regs_entry.value = inst;
181 const spilled_inst = self.registers[index].?;
182 self.registers[index] = inst;
184183 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
185184 } else {
186185 self.getRegAssumeFree(reg, inst);
......@@ -190,34 +189,33 @@ pub fn RegisterManager(
190189 /// Spills the register if it is currently allocated.
191190 /// Does not track the register.
192191 pub fn getRegWithoutTracking(self: *Self, reg: Register) !void {
193 if (!isTracked(reg)) return;
192 const index = reg.allocIndex() orelse return;
194193
195194 if (!self.isRegFree(reg)) {
196195 // Move the instruction that was previously there to a
197196 // stack allocation.
198 const regs_entry = self.registers.remove(reg).?;
199 const spilled_inst = regs_entry.value;
197 const spilled_inst = self.registers[index].?;
200198 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
201199 self.markRegFree(reg);
202200 }
203201 }
204202
205203 /// Allocates the specified register with the specified
206 /// instruction. Assumes that the register is free and no
204 /// instruction. Asserts that the register is free and no
207205 /// spilling is necessary.
208 /// Before calling, must ensureCapacity + 1 on self.registers.
209206 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void {
210 if (!isTracked(reg)) return;
207 const index = reg.allocIndex() orelse return;
211208
212 self.registers.putAssumeCapacityNoClobber(reg, inst);
209 assert(self.registers[index] == null);
210 self.registers[index] = inst;
213211 self.markRegUsed(reg);
214212 }
215213
216214 /// Marks the specified register as free
217215 pub fn freeReg(self: *Self, reg: Register) void {
218 if (!isTracked(reg)) return;
216 const index = reg.allocIndex() orelse return;
219217
220 _ = self.registers.remove(reg);
218 self.registers[index] = null;
221219 self.markRegFree(reg);
222220 }
223221 };
......@@ -247,7 +245,6 @@ const MockFunction = struct {
247245 const Self = @This();
248246
249247 pub fn deinit(self: *Self) void {
250 self.register_manager.deinit(self.allocator);
251248 self.spilled.deinit(self.allocator);
252249 }
253250
......@@ -273,7 +270,6 @@ test "tryAllocReg: no spilling" {
273270 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
274271 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
275272
276 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
277273 std.testing.expectEqual(@as(?MockRegister, .r2), function.register_manager.tryAllocReg(&mock_instruction));
278274 std.testing.expectEqual(@as(?MockRegister, .r3), function.register_manager.tryAllocReg(&mock_instruction));
279275 std.testing.expectEqual(@as(?MockRegister, null), function.register_manager.tryAllocReg(&mock_instruction));
......@@ -305,7 +301,6 @@ test "allocReg: spilling" {
305301 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
306302 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
307303
308 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
309304 std.testing.expectEqual(@as(?MockRegister, .r2), try function.register_manager.allocReg(&mock_instruction));
310305 std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction));
311306
......@@ -336,14 +331,12 @@ test "getReg" {
336331 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
337332 std.testing.expect(!function.register_manager.isRegAllocated(.r3));
338333
339 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
340334 try function.register_manager.getReg(.r3, &mock_instruction);
341335
342336 std.testing.expect(!function.register_manager.isRegAllocated(.r2));
343337 std.testing.expect(function.register_manager.isRegAllocated(.r3));
344338
345339 // Spill r3
346 try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2);
347340 try function.register_manager.getReg(.r3, &mock_instruction);
348341
349342 std.testing.expect(!function.register_manager.isRegAllocated(.r2));