| ... | ... | @@ -18,6 +18,7 @@ const leb128 = std.leb; |
| 18 | 18 | const log = std.log.scoped(.codegen); |
| 19 | 19 | const build_options = @import("build_options"); |
| 20 | 20 | const LazySrcLoc = Module.LazySrcLoc; |
| 21 | const RegisterManager = @import("register_manager.zig").RegisterManager; |
| 21 | 22 | |
| 22 | 23 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. |
| 23 | 24 | pub const BlockData = struct { |
| ... | ... | @@ -286,11 +287,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 286 | 287 | /// across each runtime branch upon joining. |
| 287 | 288 | branch_stack: *std.ArrayList(Branch), |
| 288 | 289 | |
| 289 | | /// The key must be canonical register. |
| 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, |
| 290 | register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{}, |
| 294 | 291 | /// Maps offset to what is stored there. |
| 295 | 292 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 296 | 293 | |
| ... | ... | @@ -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 | 382 | const StackAllocation = struct { |
| 429 | 383 | inst: *ir.Inst, |
| 430 | 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 | 449 | .rbrace_src = src_data.rbrace_src, |
| 496 | 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 | 453 | defer function.stack.deinit(bin_file.allocator); |
| 500 | 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 | 561 | .r14 = true, // lr |
| 608 | 562 | }; |
| 609 | 563 | inline for (callee_preserved_regs) |reg, i| { |
| 610 | | const ShiftInt = math.Log2Int(FreeRegInt); |
| 611 | | const shift = @intCast(ShiftInt, i); |
| 612 | | const mask = @as(FreeRegInt, 1) << shift; |
| 613 | | if (self.allocated_registers & mask != 0) { |
| 564 | if (self.register_manager.isRegAllocated(reg)) { |
| 614 | 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 | 780 | switch (prev_value) { |
| 830 | 781 | .register => |reg| { |
| 831 | 782 | const canon_reg = toCanonicalReg(reg); |
| 832 | | _ = self.registers.remove(canon_reg); |
| 833 | | self.markRegFree(canon_reg); |
| 783 | self.register_manager.freeReg(canon_reg); |
| 834 | 784 | }, |
| 835 | 785 | else => {}, // TODO process stack allocation death |
| 836 | 786 | } |
| ... | ... | @@ -969,8 +919,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 969 | 919 | const ptr_bits = arch.ptrBitWidth(); |
| 970 | 920 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 971 | 921 | if (abi_size <= ptr_bytes) { |
| 972 | | try self.registers.ensureCapacity(self.gpa, self.registers.count() + 1); |
| 973 | | if (self.allocReg(inst)) |reg| { |
| 922 | try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1); |
| 923 | if (self.register_manager.tryAllocReg(inst)) |reg| { |
| 974 | 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 | 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 | 941 | /// Copies a value to a register without tracking the register. The register is not considered |
| 983 | 942 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 984 | 943 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 985 | 944 | fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register { |
| 986 | | const reg = self.findUnusedReg() orelse b: { |
| 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 | | }; |
| 945 | const reg = try self.register_manager.allocRegWithoutTracking(); |
| 1002 | 946 | try self.genSetReg(src, ty, reg, mcv); |
| 1003 | 947 | return reg; |
| 1004 | 948 | } |
| ... | ... | @@ -1007,25 +951,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1007 | 951 | /// `reg_owner` is the instruction that gets associated with the register in the register table. |
| 1008 | 952 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 1009 | 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)); |
| 1011 | | |
| 1012 | | const reg = self.allocReg(reg_owner) orelse b: { |
| 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 | | }; |
| 954 | try self.register_manager.registers.ensureCapacity(self.gpa, @intCast(u32, self.register_manager.registers.count() + 1)); |
| 955 | |
| 956 | const reg = try self.register_manager.allocReg(reg_owner); |
| 1029 | 957 | try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv); |
| 1030 | 958 | return MCValue{ .register = reg }; |
| 1031 | 959 | } |
| ... | ... | @@ -1302,7 +1230,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1302 | 1230 | .register => |reg| { |
| 1303 | 1231 | // If it's in the registers table, need to associate the register with the |
| 1304 | 1232 | // new instruction. |
| 1305 | | if (self.registers.getEntry(toCanonicalReg(reg))) |entry| { |
| 1233 | if (self.register_manager.registers.getEntry(toCanonicalReg(reg))) |entry| { |
| 1306 | 1234 | entry.value = inst; |
| 1307 | 1235 | } |
| 1308 | 1236 | log.debug("reusing {} => {*}", .{ reg, inst }); |
| ... | ... | @@ -1795,7 +1723,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1795 | 1723 | const arg_index = self.arg_index; |
| 1796 | 1724 | self.arg_index += 1; |
| 1797 | 1725 | |
| 1798 | | if (FreeRegInt == u0) { |
| 1726 | if (callee_preserved_regs.len == 0) { |
| 1799 | 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 | 1735 | |
| 1808 | 1736 | switch (result) { |
| 1809 | 1737 | .register => |reg| { |
| 1810 | | try self.registers.putNoClobber(self.gpa, toCanonicalReg(reg), &inst.base); |
| 1811 | | self.markRegUsed(reg); |
| 1738 | try self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base); |
| 1812 | 1739 | }, |
| 1813 | 1740 | else => {}, |
| 1814 | 1741 | } |
| ... | ... | @@ -2431,10 +2358,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2431 | 2358 | |
| 2432 | 2359 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 2433 | 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 | 2362 | var parent_stack = try self.stack.clone(self.gpa); |
| 2436 | 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 | 2365 | defer parent_registers.deinit(self.gpa); |
| 2439 | 2366 | |
| 2440 | 2367 | try self.branch_stack.append(.{}); |
| ... | ... | @@ -2451,8 +2378,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2451 | 2378 | var saved_then_branch = self.branch_stack.pop(); |
| 2452 | 2379 | defer saved_then_branch.deinit(self.gpa); |
| 2453 | 2380 | |
| 2454 | | self.registers.deinit(self.gpa); |
| 2455 | | self.registers = parent_registers; |
| 2381 | self.register_manager.registers.deinit(self.gpa); |
| 2382 | self.register_manager.registers = parent_registers; |
| 2456 | 2383 | parent_registers = .{}; |
| 2457 | 2384 | |
| 2458 | 2385 | self.stack.deinit(self.gpa); |
| ... | ... | @@ -2460,7 +2387,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2460 | 2387 | parent_stack = .{}; |
| 2461 | 2388 | |
| 2462 | 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 | 2392 | try self.performReloc(inst.base.src, reloc); |
| 2466 | 2393 | const else_branch = self.branch_stack.addOneAssumeCapacity(); |
| ... | ... | @@ -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 | 3979 | fn parseRegName(name: []const u8) ?Register { |
| 4056 | 3980 | if (@hasDecl(Register, "parseRegName")) { |
| 4057 | 3981 | return Register.parseRegName(name); |