| ... | @@ -18,6 +18,7 @@ const leb128 = std.leb; | ... | @@ -18,6 +18,7 @@ const leb128 = std.leb; |
| 18 | const log = std.log.scoped(.codegen); | 18 | const log = std.log.scoped(.codegen); |
| 19 | const build_options = @import("build_options"); | 19 | const build_options = @import("build_options"); |
| 20 | const LazySrcLoc = Module.LazySrcLoc; | 20 | const LazySrcLoc = Module.LazySrcLoc; |
| | 21 | const RegisterManager = @import("register_manager.zig").RegisterManager; |
| 21 | | 22 | |
| 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. |
| 23 | pub const BlockData = struct { | 24 | pub 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), |
| 288 | | 289 | |
| 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) = .{}, |
| 296 | | 293 | |
| ... | @@ -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 | }; |
| 384 | | 381 | |
| 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); |
| 501 | | 455 | |
| ... | @@ -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, // lr | 561 | .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 death | 785 | 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 | } |
| 981 | | 931 | |
| | 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 considered | 941 | /// 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)); |
| 1011 | | 955 | |
| 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 the | 1231 | // 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; |
| 1797 | | 1725 | |
| 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 | } |
| 1801 | | 1729 | |
| ... | @@ -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 { |
| 1807 | | 1735 | |
| 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 { |
| 2431 | | 2358 | |
| 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); |
| 2439 | | 2366 | |
| 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); |
| 2453 | | 2380 | |
| 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 = .{}; |
| 2457 | | 2384 | |
| 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 = .{}; |
| 2461 | | 2388 | |
| 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; |
| 2464 | | 2391 | |
| 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 | }; |
| 4051 | | 3978 | |
| 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); |