authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-03-19 10:11:45+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-02 12:01:52-07:00
log4efbcad26d088a0b40cae308ca2990a71ea1f3f9
tree299e234e4354a6a7da0e27d90e7476aa8eb27e34
parentfc9c1b4e4ae86c36a7ff29859cc589a9816ab7d5

stage2 codegen: Extract register management code into separate file


2 files changed, 155 insertions(+), 104 deletions(-)

src/codegen.zig+28-104
...@@ -18,6 +18,7 @@ const leb128 = std.leb;...@@ -18,6 +18,7 @@ const leb128 = std.leb;
18const log = std.log.scoped(.codegen);18const log = std.log.scoped(.codegen);
19const build_options = @import("build_options");19const build_options = @import("build_options");
20const LazySrcLoc = Module.LazySrcLoc;20const LazySrcLoc = Module.LazySrcLoc;
21const RegisterManager = @import("register_manager.zig").RegisterManager;
2122
22/// The codegen-related data that is stored in `ir.Inst.Block` instructions.23/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
23pub const BlockData = struct {24pub const BlockData = struct {
...@@ -286,11 +287,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -286,11 +287,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
286 /// across each runtime branch upon joining.287 /// across each runtime branch upon joining.
287 branch_stack: *std.ArrayList(Branch),288 branch_stack: *std.ArrayList(Branch),
288289
289 /// The key must be canonical register.290 register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
290 registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
291 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
292 /// Tracks all registers allocated in the course of this function
293 allocated_registers: FreeRegInt = 0,
294 /// Maps offset to what is stored there.291 /// Maps offset to what is stored there.
295 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},292 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
296293
...@@ -382,49 +379,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -382,49 +379,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
382 }379 }
383 };380 };
384381
385 fn markRegUsed(self: *Self, reg: Register) void {
386 if (FreeRegInt == u0) return;
387 const index = reg.allocIndex() orelse return;
388 const ShiftInt = math.Log2Int(FreeRegInt);
389 const shift = @intCast(ShiftInt, index);
390 const mask = @as(FreeRegInt, 1) << shift;
391 self.free_registers &= ~mask;
392 self.allocated_registers |= mask;
393 }
394
395 fn markRegFree(self: *Self, reg: Register) void {
396 if (FreeRegInt == u0) return;
397 const index = reg.allocIndex() orelse return;
398 const ShiftInt = math.Log2Int(FreeRegInt);
399 const shift = @intCast(ShiftInt, index);
400 self.free_registers |= @as(FreeRegInt, 1) << shift;
401 }
402
403 /// Before calling, must ensureCapacity + 1 on self.registers.
404 /// Returns `null` if all registers are allocated.
405 fn allocReg(self: *Self, inst: *ir.Inst) ?Register {
406 const free_index = @ctz(FreeRegInt, self.free_registers);
407 if (free_index >= callee_preserved_regs.len) {
408 return null;
409 }
410 const mask = @as(FreeRegInt, 1) << free_index;
411 self.free_registers &= ~mask;
412 self.allocated_registers |= mask;
413 const reg = callee_preserved_regs[free_index];
414 self.registers.putAssumeCapacityNoClobber(reg, inst);
415 log.debug("alloc {} => {*}", .{ reg, inst });
416 return reg;
417 }
418
419 /// Does not track the register.
420 fn findUnusedReg(self: *Self) ?Register {
421 const free_index = @ctz(FreeRegInt, self.free_registers);
422 if (free_index >= callee_preserved_regs.len) {
423 return null;
424 }
425 return callee_preserved_regs[free_index];
426 }
427
428 const StackAllocation = struct {382 const StackAllocation = struct {
429 inst: *ir.Inst,383 inst: *ir.Inst,
430 /// TODO do we need size? should be determined by inst.ty.abiSize()384 /// TODO do we need size? should be determined by inst.ty.abiSize()
...@@ -495,7 +449,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -495,7 +449,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
495 .rbrace_src = src_data.rbrace_src,449 .rbrace_src = src_data.rbrace_src,
496 .source = src_data.source,450 .source = src_data.source,
497 };451 };
498 defer function.registers.deinit(bin_file.allocator);452 defer function.register_manager.deinit(bin_file.allocator);
499 defer function.stack.deinit(bin_file.allocator);453 defer function.stack.deinit(bin_file.allocator);
500 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);454 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
501455
...@@ -607,10 +561,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -607,10 +561,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
607 .r14 = true, // lr561 .r14 = true, // lr
608 };562 };
609 inline for (callee_preserved_regs) |reg, i| {563 inline for (callee_preserved_regs) |reg, i| {
610 const ShiftInt = math.Log2Int(FreeRegInt);564 if (self.register_manager.isRegAllocated(reg)) {
611 const shift = @intCast(ShiftInt, i);
612 const mask = @as(FreeRegInt, 1) << shift;
613 if (self.allocated_registers & mask != 0) {
614 @field(saved_regs, @tagName(reg)) = true;565 @field(saved_regs, @tagName(reg)) = true;
615 }566 }
616 }567 }
...@@ -829,8 +780,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -829,8 +780,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
829 switch (prev_value) {780 switch (prev_value) {
830 .register => |reg| {781 .register => |reg| {
831 const canon_reg = toCanonicalReg(reg);782 const canon_reg = toCanonicalReg(reg);
832 _ = self.registers.remove(canon_reg);783 self.register_manager.freeReg(canon_reg);
833 self.markRegFree(canon_reg);
834 },784 },
835 else => {}, // TODO process stack allocation death785 else => {}, // TODO process stack allocation death
836 }786 }
...@@ -969,8 +919,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -969,8 +919,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
969 const ptr_bits = arch.ptrBitWidth();919 const ptr_bits = arch.ptrBitWidth();
970 const ptr_bytes: u64 = @divExact(ptr_bits, 8);920 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
971 if (abi_size <= ptr_bytes) {921 if (abi_size <= ptr_bytes) {
972 try self.registers.ensureCapacity(self.gpa, self.registers.count() + 1);922 try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1);
973 if (self.allocReg(inst)) |reg| {923 if (self.register_manager.tryAllocReg(inst)) |reg| {
974 return MCValue{ .register = registerAlias(reg, abi_size) };924 return MCValue{ .register = registerAlias(reg, abi_size) };
975 }925 }
976 }926 }
...@@ -979,26 +929,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -979,26 +929,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
979 return MCValue{ .stack_offset = stack_offset };929 return MCValue{ .stack_offset = stack_offset };
980 }930 }
981931
932 pub fn spillInstruction(self: *Self, src: usize, reg: Register, inst: *ir.Inst) !void {
933 const stack_mcv = try self.allocRegOrMem(inst, false);
934 const reg_mcv = self.getResolvedInstValue(inst);
935 assert(reg == toCanonicalReg(reg_mcv.register));
936 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
937 try branch.inst_table.put(self.gpa, inst, stack_mcv);
938 try self.genSetStack(src, inst.ty, stack_mcv.stack_offset, reg_mcv);
939 }
940
982 /// Copies a value to a register without tracking the register. The register is not considered941 /// Copies a value to a register without tracking the register. The register is not considered
983 /// allocated. A second call to `copyToTmpRegister` may return the same register.942 /// allocated. A second call to `copyToTmpRegister` may return the same register.
984 /// This can have a side effect of spilling instructions to the stack to free up a register.943 /// This can have a side effect of spilling instructions to the stack to free up a register.
985 fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register {944 fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register {
986 const reg = self.findUnusedReg() orelse b: {945 const reg = try self.register_manager.allocRegWithoutTracking();
987 // We'll take over the first register. Move the instruction that was previously
988 // there to a stack allocation.
989 const reg = callee_preserved_regs[0];
990 const regs_entry = self.registers.remove(reg).?;
991 const spilled_inst = regs_entry.value;
992
993 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
994 const reg_mcv = self.getResolvedInstValue(spilled_inst);
995 assert(reg == toCanonicalReg(reg_mcv.register));
996 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
997 try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
998 try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
999
1000 break :b reg;
1001 };
1002 try self.genSetReg(src, ty, reg, mcv);946 try self.genSetReg(src, ty, reg, mcv);
1003 return reg;947 return reg;
1004 }948 }
...@@ -1007,25 +951,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1007,25 +951,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1007 /// `reg_owner` is the instruction that gets associated with the register in the register table.951 /// `reg_owner` is the instruction that gets associated with the register in the register table.
1008 /// This can have a side effect of spilling instructions to the stack to free up a register.952 /// This can have a side effect of spilling instructions to the stack to free up a register.
1009 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {953 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
1010 try self.registers.ensureCapacity(self.gpa, @intCast(u32, self.registers.count() + 1));954 try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1));
1011955
1012 const reg = self.allocReg(reg_owner) orelse b: {956 const reg = try self.register_manager.allocReg(reg_owner);
1013 // We'll take over the first register. Move the instruction that was previously
1014 // there to a stack allocation.
1015 const reg = callee_preserved_regs[0];
1016 const regs_entry = self.registers.getEntry(reg).?;
1017 const spilled_inst = regs_entry.value;
1018 regs_entry.value = reg_owner;
1019
1020 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
1021 const reg_mcv = self.getResolvedInstValue(spilled_inst);
1022 assert(reg == toCanonicalReg(reg_mcv.register));
1023 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1024 try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
1025 try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
1026
1027 break :b reg;
1028 };
1029 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);957 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);
1030 return MCValue{ .register = reg };958 return MCValue{ .register = reg };
1031 }959 }
...@@ -1302,7 +1230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1302,7 +1230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1302 .register => |reg| {1230 .register => |reg| {
1303 // If it's in the registers table, need to associate the register with the1231 // If it's in the registers table, need to associate the register with the
1304 // new instruction.1232 // new instruction.
1305 if (self.registers.getEntry(toCanonicalReg(reg))) |entry| {1233 if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| {
1306 entry.value = inst;1234 entry.value = inst;
1307 }1235 }
1308 log.debug("reusing {} => {*}", .{ reg, inst });1236 log.debug("reusing {} => {*}", .{ reg, inst });
...@@ -1795,7 +1723,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1795,7 +1723,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1795 const arg_index = self.arg_index;1723 const arg_index = self.arg_index;
1796 self.arg_index += 1;1724 self.arg_index += 1;
17971725
1798 if (FreeRegInt == u0) {1726 if (callee_preserved_regs.len == 0) {
1799 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});1727 return self.fail(inst.base.src, "TODO implement Register enum for {}", .{self.target.cpu.arch});
1800 }1728 }
18011729
...@@ -1807,8 +1735,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1807,8 +1735,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18071735
1808 switch (result) {1736 switch (result) {
1809 .register => |reg| {1737 .register => |reg| {
1810 try self.registers.putNoClobber(self.gpa, toCanonicalReg(reg), &inst.base);1738 try self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);
1811 self.markRegUsed(reg);
1812 },1739 },
1813 else => {},1740 else => {},
1814 }1741 }
...@@ -2431,10 +2358,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2431,10 +2358,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
24312358
2432 // Capture the state of register and stack allocation state so that we can revert to it.2359 // Capture the state of register and stack allocation state so that we can revert to it.
2433 const parent_next_stack_offset = self.next_stack_offset;2360 const parent_next_stack_offset = self.next_stack_offset;
2434 const parent_free_registers = self.free_registers;2361 const parent_free_registers = self.register_manager.free_registers;
2435 var parent_stack = try self.stack.clone(self.gpa);2362 var parent_stack = try self.stack.clone(self.gpa);
2436 defer parent_stack.deinit(self.gpa);2363 defer parent_stack.deinit(self.gpa);
2437 var parent_registers = try self.registers.clone(self.gpa);2364 var parent_registers = try self.register_manager.registers.clone(self.gpa);
2438 defer parent_registers.deinit(self.gpa);2365 defer parent_registers.deinit(self.gpa);
24392366
2440 try self.branch_stack.append(.{});2367 try self.branch_stack.append(.{});
...@@ -2451,8 +2378,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2451,8 +2378,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2451 var saved_then_branch = self.branch_stack.pop();2378 var saved_then_branch = self.branch_stack.pop();
2452 defer saved_then_branch.deinit(self.gpa);2379 defer saved_then_branch.deinit(self.gpa);
24532380
2454 self.registers.deinit(self.gpa);2381 self.register_manager.registers.deinit(self.gpa);
2455 self.registers = parent_registers;2382 self.register_manager.registers = parent_registers;
2456 parent_registers = .{};2383 parent_registers = .{};
24572384
2458 self.stack.deinit(self.gpa);2385 self.stack.deinit(self.gpa);
...@@ -2460,7 +2387,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2460,7 +2387,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2460 parent_stack = .{};2387 parent_stack = .{};
24612388
2462 self.next_stack_offset = parent_next_stack_offset;2389 self.next_stack_offset = parent_next_stack_offset;
2463 self.free_registers = parent_free_registers;2390 self.register_manager.free_registers = parent_free_registers;
24642391
2465 try self.performReloc(inst.base.src, reloc);2392 try self.performReloc(inst.base.src, reloc);
2466 const else_branch = self.branch_stack.addOneAssumeCapacity();2393 const else_branch = self.branch_stack.addOneAssumeCapacity();
...@@ -4049,9 +3976,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4049,9 +3976,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4049 },3976 },
4050 };3977 };
40513978
4052 /// An integer whose bits represent all the registers and whether they are free.
4053 const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len);
4054
4055 fn parseRegName(name: []const u8) ?Register {3979 fn parseRegName(name: []const u8) ?Register {
4056 if (@hasDecl(Register, "parseRegName")) {3980 if (@hasDecl(Register, "parseRegName")) {
4057 return Register.parseRegName(name);3981 return Register.parseRegName(name);
src/register_manager.zig created+127
...@@ -0,0 +1,127 @@
1const std = @import("std");
2const math = std.math;
3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;
5const ir = @import("ir.zig");
6const Type = @import("type.zig").Type;
7const log = std.log.scoped(.register_manager);
8
9pub fn RegisterManager(
10 comptime Function: type,
11 comptime Register: type,
12 comptime callee_preserved_regs: []const Register,
13) type {
14 return struct {
15 /// The key must be canonical register.
16 registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
17 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
18 /// Tracks all registers allocated in the course of this function
19 allocated_registers: FreeRegInt = 0,
20
21 const Self = @This();
22
23 /// An integer whose bits represent all the registers and whether they are free.
24 const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len);
25
26 fn getFunction(self: *Self) *Function {
27 return @fieldParentPtr(Function, "register_manager", self);
28 }
29
30 pub fn deinit(self: *Self, allocator: *Allocator) void {
31 self.registers.deinit(allocator);
32 }
33
34 fn markRegUsed(self: *Self, reg: Register) void {
35 if (FreeRegInt == u0) return;
36 const index = reg.allocIndex() orelse return;
37 const ShiftInt = math.Log2Int(FreeRegInt);
38 const shift = @intCast(ShiftInt, index);
39 const mask = @as(FreeRegInt, 1) << shift;
40 self.free_registers &= ~mask;
41 self.allocated_registers |= mask;
42 }
43
44 fn markRegFree(self: *Self, reg: Register) void {
45 if (FreeRegInt == u0) return;
46 const index = reg.allocIndex() orelse return;
47 const ShiftInt = math.Log2Int(FreeRegInt);
48 const shift = @intCast(ShiftInt, index);
49 self.free_registers |= @as(FreeRegInt, 1) << shift;
50 }
51
52 /// Returns whether this register was allocated in the course
53 /// of this function
54 pub fn isRegAllocated(self: Self, reg: Register) bool {
55 if (FreeRegInt == u0) return false;
56 const index = reg.allocIndex() orelse return false;
57 const ShiftInt = math.Log2Int(FreeRegInt);
58 const shift = @intCast(ShiftInt, index);
59 return self.free_registers & @as(FreeRegInt, 1) << shift != 0;
60 }
61
62 /// Before calling, must ensureCapacity + 1 on self.registers.
63 /// Returns `null` if all registers are allocated.
64 pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register {
65 const free_index = @ctz(FreeRegInt, self.free_registers);
66 if (free_index >= callee_preserved_regs.len) {
67 return null;
68 }
69 const mask = @as(FreeRegInt, 1) << free_index;
70 self.free_registers &= ~mask;
71 self.allocated_registers |= mask;
72 const reg = callee_preserved_regs[free_index];
73 self.registers.putAssumeCapacityNoClobber(reg, inst);
74 log.debug("alloc {} => {*}", .{ reg, inst });
75 return reg;
76 }
77
78 /// Before calling, must ensureCapacity + 1 on self.registers.
79 pub fn allocReg(self: *Self, inst: *ir.Inst) !Register {
80 return self.tryAllocReg(inst) orelse b: {
81 // We'll take over the first register. Move the instruction that was previously
82 // there to a stack allocation.
83 const reg = callee_preserved_regs[0];
84 const regs_entry = self.registers.getEntry(reg).?;
85 const spilled_inst = regs_entry.value;
86 regs_entry.value = inst;
87 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
88
89 break :b reg;
90 };
91 }
92
93 /// Does not track the register.
94 /// Returns `null` if all registers are allocated.
95 pub fn findUnusedReg(self: *Self) ?Register {
96 const free_index = @ctz(FreeRegInt, self.free_registers);
97 if (free_index >= callee_preserved_regs.len) {
98 return null;
99 }
100 return callee_preserved_regs[free_index];
101 }
102
103 /// Does not track the register.
104 pub fn allocRegWithoutTracking(self: *Self) !Register {
105 return self.findUnusedReg() orelse b: {
106 // We'll take over the first register. Move the instruction that was previously
107 // there to a stack allocation.
108 const reg = callee_preserved_regs[0];
109 const regs_entry = self.registers.remove(reg).?;
110 const spilled_inst = regs_entry.value;
111 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);
112
113 break :b reg;
114 };
115 }
116
117 pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) !void {
118 try self.registers.putNoClobber(self.getFunction().gpa, reg, inst);
119 self.markRegUsed(reg);
120 }
121
122 pub fn freeReg(self: *Self, reg: Register) void {
123 _ = self.registers.remove(reg);
124 self.markRegFree(reg);
125 }
126 };
127}