authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-04-11 14:48:49+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-16 13:03:10-04:00
log5edabb3990c9a1dce61e1c3957a218d635e27210
tree80aad2793afb6e9ce52959f00fa7e5ec16d0863c
parent2cd49d20e859af421d371d1c83349273819648d8

stage2 register manager: Add functions for allocating multiple registers


1 files changed, 74 insertions(+), 37 deletions(-)

src/register_manager.zig+74-37
......@@ -36,7 +36,7 @@ pub fn RegisterManager(
3636 }
3737
3838 fn isTracked(reg: Register) bool {
39 return std.mem.indexOfScalar(Register, callee_preserved_regs, reg) != null;
39 return reg.allocIndex() != null;
4040 }
4141
4242 fn markRegUsed(self: *Self, reg: Register) void {
......@@ -55,6 +55,7 @@ pub fn RegisterManager(
5555 self.free_registers |= @as(FreeRegInt, 1) << shift;
5656 }
5757
58 /// Returns true when this register is not tracked
5859 pub fn isRegFree(self: Self, reg: Register) bool {
5960 if (FreeRegInt == u0) return true;
6061 const index = reg.allocIndex() orelse return true;
......@@ -63,7 +64,8 @@ pub fn RegisterManager(
6364 }
6465
6566 /// Returns whether this register was allocated in the course
66 /// of this function
67 /// of this function.
68 /// Returns false when this register is not tracked
6769 pub fn isRegAllocated(self: Self, reg: Register) bool {
6870 if (FreeRegInt == u0) return false;
6971 const index = reg.allocIndex() orelse return false;
......@@ -71,57 +73,89 @@ pub fn RegisterManager(
7173 return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0;
7274 }
7375
74 /// Before calling, must ensureCapacity + 1 on self.registers.
76 /// Before calling, must ensureCapacity + count on self.registers.
7577 /// Returns `null` if all registers are allocated.
76 pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
77 const free_index = @ctz(FreeRegInt, self.free_registers);
78 if (free_index >= callee_preserved_regs.len) {
78 pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register {
79 if (self.tryAllocRegsWithoutTracking(count)) |regs| {
80 for (regs) |reg, i| {
81 self.markRegUsed(reg);
82 self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
83 }
84
85 return regs;
86 } else {
7987 return null;
8088 }
89 }
8190
82 // This is necessary because the return type of @ctz is 1
83 // bit longer than ShiftInt if callee_preserved_regs.len
84 // is a power of two. This int cast is always safe because
85 // free_index < callee_preserved_regs.len
86 const shift = @intCast(ShiftInt, free_index);
87 const mask = @as(FreeRegInt, 1) << shift;
88 self.free_registers &= ~mask;
89 self.allocated_registers |= mask;
91 /// Before calling, must ensureCapacity + 1 on self.registers.
92 /// Returns `null` if all registers are allocated.
93 pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
94 return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null;
95 }
9096
91 const reg = callee_preserved_regs[free_index];
92 self.registers.putAssumeCapacityNoClobber(reg, inst);
93 log.debug("alloc {} => {*}", .{ reg, inst });
94 return reg;
97 /// Before calling, must ensureCapacity + count on self.registers.
98 pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register {
99 comptime assert(count > 0 and count <= callee_preserved_regs.len);
100
101 return self.tryAllocRegs(count, insts) orelse blk: {
102 // We'll take over the first count registers. Spill
103 // the instructions that were previously there to a
104 // stack allocations.
105 var regs: [count]Register = undefined;
106 std.mem.copy(Register, &regs, callee_preserved_regs[0..count]);
107
108 for (regs) |reg, i| {
109 if (self.isRegFree(reg)) {
110 self.markRegUsed(reg);
111 self.registers.putAssumeCapacityNoClobber(reg, insts[i]);
112 } else {
113 const regs_entry = self.registers.getEntry(reg).?;
114 const spilled_inst = regs_entry.value;
115 regs_entry.value = insts[i];
116 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
117 }
118 }
119
120 break :blk regs;
121 };
95122 }
96123
97124 /// Before calling, must ensureCapacity + 1 on self.registers.
98125 pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {
99 return self.tryAllocReg(inst) orelse b: {
100 // We'll take over the first register. Move the instruction that was previously
101 // there to a stack allocation.
102 const reg = callee_preserved_regs[0];
103 const regs_entry = self.registers.getEntry(reg).?;
104 const spilled_inst = regs_entry.value;
105 regs_entry.value = inst;
106 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
126 return (try allocRegs(self, 1, .{inst}))[0];
127 }
107128
108 break :b reg;
109 };
129 /// Does not track the registers.
130 /// Returns `null` if not enough registers are free.
131 pub fn tryAllocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ?[count]Register {
132 comptime if (callee_preserved_regs.len == 0) return null;
133 comptime assert(count > 0 and count <= callee_preserved_regs.len);
134
135 const free_registers = @popCount(FreeRegInt, self.free_registers);
136 if (free_registers < count) return null;
137
138 var regs: [count]Register = undefined;
139 var i: usize = 0;
140 for (callee_preserved_regs) |reg| {
141 if (i >= count) break;
142 if (self.isRegFree(reg)) {
143 regs[i] = reg;
144 i += 1;
145 }
146 }
147 return regs;
110148 }
111149
112150 /// Does not track the register.
113151 /// Returns `null` if all registers are allocated.
114 pub fn findUnusedReg(self: *Self) ?Register {
115 const free_index = @ctz(FreeRegInt, self.free_registers);
116 if (free_index >= callee_preserved_regs.len) {
117 return null;
118 }
119 return callee_preserved_regs[free_index];
152 pub fn tryAllocRegWithoutTracking(self: *Self) ?Register {
153 return if (tryAllocRegsWithoutTracking(self, 1)) |regs| regs[0] else null;
120154 }
121155
122156 /// Does not track the register.
123157 pub fn allocRegWithoutTracking(self: *Self) !Register {
124 return self.findUnusedReg() orelse b: {
158 return self.tryAllocRegWithoutTracking() orelse b: {
125159 // We'll take over the first register. Move the instruction that was previously
126160 // there to a stack allocation.
127161 const reg = callee_preserved_regs[0];
......@@ -190,7 +224,10 @@ pub fn RegisterManager(
190224}
191225
192226const MockRegister = enum(u2) {
193 r0, r1, r2, r3,
227 r0,
228 r1,
229 r2,
230 r3,
194231
195232 pub fn allocIndex(self: MockRegister) ?u2 {
196233 inline for (mock_callee_preserved_regs) |cpreg, i| {
......@@ -213,7 +250,7 @@ const MockFunction = struct {
213250 self.register_manager.deinit(self.allocator);
214251 self.spilled.deinit(self.allocator);
215252 }
216
253
217254 pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void {
218255 try self.spilled.append(self.allocator, reg);
219256 }