| author | |
| committer | |
| log | 4590e980f77f406158258c8f44a76e28d53eff2f |
| tree | ba874ceb64ee6c2de89223dbf87d4b0e0018d78f |
| parent | 06058ed6f38194dfe74d232e77e33d98919d1ee7 |
| signature |
3 files changed, 40 insertions(+), 32 deletions(-)
src/arch/arm/CodeGen.zig+21-18| ... | @@ -22,7 +22,7 @@ const leb128 = std.leb; | ... | @@ -22,7 +22,7 @@ const leb128 = std.leb; |
| 22 | const log = std.log.scoped(.codegen); | 22 | const log = std.log.scoped(.codegen); |
| 23 | const build_options = @import("build_options"); | 23 | const build_options = @import("build_options"); |
| 24 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; | 24 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; |
| 25 | const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); | 25 | const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); |
| 26 | 26 | ||
| 27 | const FnResult = @import("../../codegen.zig").FnResult; | 27 | const FnResult = @import("../../codegen.zig").FnResult; |
| 28 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | 28 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| ... | @@ -34,6 +34,8 @@ const Register = bits.Register; | ... | @@ -34,6 +34,8 @@ const Register = bits.Register; |
| 34 | const Instruction = bits.Instruction; | 34 | const Instruction = bits.Instruction; |
| 35 | const Condition = bits.Condition; | 35 | const Condition = bits.Condition; |
| 36 | const callee_preserved_regs = abi.callee_preserved_regs; | 36 | const callee_preserved_regs = abi.callee_preserved_regs; |
| 37 | const caller_preserved_regs = abi.caller_preserved_regs; | ||
| 38 | const allocatable_registers = abi.allocatable_registers; | ||
| 37 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; | 39 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; |
| 38 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | 40 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; |
| 39 | 41 | ||
| ... | @@ -788,10 +790,6 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { | ... | @@ -788,10 +790,6 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 788 | if (!elem_ty.hasRuntimeBits()) { | 790 | if (!elem_ty.hasRuntimeBits()) { |
| 789 | // As this stack item will never be dereferenced at runtime, | 791 | // As this stack item will never be dereferenced at runtime, |
| 790 | // return the current stack offset | 792 | // return the current stack offset |
| 791 | try self.stack.putNoClobber(self.gpa, self.next_stack_offset, .{ | ||
| 792 | .inst = inst, | ||
| 793 | .size = 0, | ||
| 794 | }); | ||
| 795 | return self.next_stack_offset; | 793 | return self.next_stack_offset; |
| 796 | } | 794 | } |
| 797 | 795 | ||
| ... | @@ -1569,13 +1567,13 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind | ... | @@ -1569,13 +1567,13 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind |
| 1569 | 1567 | ||
| 1570 | switch (mcv) { | 1568 | switch (mcv) { |
| 1571 | .register => |reg| { | 1569 | .register => |reg| { |
| 1572 | // If it's in the registers table, need to associate the register with the | 1570 | // We assert that this register is allocatable by asking |
| 1573 | // new instruction. | 1571 | // for its index |
| 1574 | if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { | 1572 | const index = RegisterManager.indexOfRegIntoTracked(reg).?; // see note above |
| 1575 | if (!self.register_manager.isRegFree(reg)) { | 1573 | if (!self.register_manager.isRegFree(reg)) { |
| 1576 | self.register_manager.registers[index] = inst; | 1574 | self.register_manager.registers[index] = inst; |
| 1577 | } | ||
| 1578 | } | 1575 | } |
| 1576 | |||
| 1579 | log.debug("%{d} => {} (reused)", .{ inst, reg }); | 1577 | log.debug("%{d} => {} (reused)", .{ inst, reg }); |
| 1580 | }, | 1578 | }, |
| 1581 | .stack_offset => |off| { | 1579 | .stack_offset => |off| { |
| ... | @@ -2545,13 +2543,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2545,13 +2543,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2545 | // Architecture, compare flags are not preserved across | 2543 | // Architecture, compare flags are not preserved across |
| 2546 | // calls. Therefore, if some value is currently stored there, we | 2544 | // calls. Therefore, if some value is currently stored there, we |
| 2547 | // need to save it. | 2545 | // need to save it. |
| 2548 | // | ||
| 2549 | // TODO once caller-saved registers are implemented, save them | ||
| 2550 | // here too, but crucially *after* we save the compare flags as | ||
| 2551 | // saving compare flags may require a new caller-saved register | ||
| 2552 | try self.spillCompareFlagsIfOccupied(); | 2546 | try self.spillCompareFlagsIfOccupied(); |
| 2553 | 2547 | ||
| 2548 | // Save caller-saved registers, but crucially *after* we save the | ||
| 2549 | // compare flags as saving compare flags may require a new | ||
| 2550 | // caller-saved register | ||
| 2551 | for (caller_preserved_regs) |reg| { | ||
| 2552 | try self.register_manager.getReg(reg, null); | ||
| 2553 | } | ||
| 2554 | |||
| 2554 | if (info.return_value == .stack_offset) { | 2555 | if (info.return_value == .stack_offset) { |
| 2556 | log.debug("airCall: return by reference", .{}); | ||
| 2555 | const ret_ty = fn_ty.fnReturnType(); | 2557 | const ret_ty = fn_ty.fnReturnType(); |
| 2556 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | 2558 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 2557 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); | 2559 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); |
| ... | @@ -2562,7 +2564,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2562,7 +2564,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2562 | .data = ret_ty, | 2564 | .data = ret_ty, |
| 2563 | }; | 2565 | }; |
| 2564 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); | 2566 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| 2565 | try self.register_manager.getReg(.r0, inst); | 2567 | try self.register_manager.getReg(.r0, null); |
| 2566 | try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset }); | 2568 | try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset }); |
| 2567 | 2569 | ||
| 2568 | info.return_value = .{ .stack_offset = stack_offset }; | 2570 | info.return_value = .{ .stack_offset = stack_offset }; |
| ... | @@ -2662,8 +2664,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2662,8 +2664,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2662 | const result: MCValue = result: { | 2664 | const result: MCValue = result: { |
| 2663 | switch (info.return_value) { | 2665 | switch (info.return_value) { |
| 2664 | .register => |reg| { | 2666 | .register => |reg| { |
| 2665 | if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { | 2667 | if (RegisterManager.indexOfRegIntoTracked(reg) == null) { |
| 2666 | // Save function return value in a callee saved register | 2668 | // Save function return value into a tracked register |
| 2669 | log.debug("airCall: copying {} as it is not tracked", .{reg}); | ||
| 2667 | break :result try self.copyToNewRegister(inst, info.return_value); | 2670 | break :result try self.copyToNewRegister(inst, info.return_value); |
| 2668 | } | 2671 | } |
| 2669 | }, | 2672 | }, |
src/arch/arm/abi.zig+3| ... | @@ -2,5 +2,8 @@ const bits = @import("bits.zig"); | ... | @@ -2,5 +2,8 @@ const bits = @import("bits.zig"); |
| 2 | const Register = bits.Register; | 2 | const Register = bits.Register; |
| 3 | 3 | ||
| 4 | pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; | 4 | pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; |
| 5 | pub const caller_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | ||
| 6 | pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs; | ||
| 7 | |||
| 5 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | 8 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; |
| 6 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | 9 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; |
src/register_manager.zig+16-14| ... | @@ -26,11 +26,11 @@ pub const AllocateRegistersError = error{ | ... | @@ -26,11 +26,11 @@ pub const AllocateRegistersError = error{ |
| 26 | pub fn RegisterManager( | 26 | pub fn RegisterManager( |
| 27 | comptime Function: type, | 27 | comptime Function: type, |
| 28 | comptime Register: type, | 28 | comptime Register: type, |
| 29 | comptime callee_preserved_regs: []const Register, | 29 | comptime tracked_registers: []const Register, |
| 30 | ) type { | 30 | ) type { |
| 31 | // architectures which do not have a concept of registers should | 31 | // architectures which do not have a concept of registers should |
| 32 | // refrain from using RegisterManager | 32 | // refrain from using RegisterManager |
| 33 | assert(callee_preserved_regs.len > 0); // see note above | 33 | assert(tracked_registers.len > 0); // see note above |
| 34 | 34 | ||
| 35 | return struct { | 35 | return struct { |
| 36 | /// Tracks the AIR instruction allocated to every register. If | 36 | /// Tracks the AIR instruction allocated to every register. If |
| ... | @@ -38,7 +38,7 @@ pub fn RegisterManager( | ... | @@ -38,7 +38,7 @@ pub fn RegisterManager( |
| 38 | /// register is free), the value in that slot is undefined. | 38 | /// register is free), the value in that slot is undefined. |
| 39 | /// | 39 | /// |
| 40 | /// The key must be canonical register. | 40 | /// The key must be canonical register. |
| 41 | registers: [callee_preserved_regs.len]Air.Inst.Index = undefined, | 41 | registers: [tracked_registers.len]Air.Inst.Index = undefined, |
| 42 | /// Tracks which registers are free (in which case the | 42 | /// Tracks which registers are free (in which case the |
| 43 | /// corresponding bit is set to 1) | 43 | /// corresponding bit is set to 1) |
| 44 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), | 44 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), |
| ... | @@ -53,7 +53,7 @@ pub fn RegisterManager( | ... | @@ -53,7 +53,7 @@ pub fn RegisterManager( |
| 53 | 53 | ||
| 54 | /// An integer whose bits represent all the registers and | 54 | /// An integer whose bits represent all the registers and |
| 55 | /// whether they are free. | 55 | /// whether they are free. |
| 56 | const FreeRegInt = std.meta.Int(.unsigned, callee_preserved_regs.len); | 56 | const FreeRegInt = std.meta.Int(.unsigned, tracked_registers.len); |
| 57 | const ShiftInt = math.Log2Int(FreeRegInt); | 57 | const ShiftInt = math.Log2Int(FreeRegInt); |
| 58 | 58 | ||
| 59 | fn getFunction(self: *Self) *Function { | 59 | fn getFunction(self: *Self) *Function { |
| ... | @@ -83,14 +83,14 @@ pub fn RegisterManager( | ... | @@ -83,14 +83,14 @@ pub fn RegisterManager( |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) { | 85 | pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) { |
| 86 | inline for (callee_preserved_regs) |cpreg, i| { | 86 | inline for (tracked_registers) |cpreg, i| { |
| 87 | if (reg.id() == cpreg.id()) return i; | 87 | if (reg.id() == cpreg.id()) return i; |
| 88 | } | 88 | } |
| 89 | return null; | 89 | return null; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt { | 92 | pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt { |
| 93 | return indexOfReg(callee_preserved_regs, reg); | 93 | return indexOfReg(tracked_registers, reg); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | /// Returns true when this register is not tracked | 96 | /// Returns true when this register is not tracked |
| ... | @@ -146,14 +146,14 @@ pub fn RegisterManager( | ... | @@ -146,14 +146,14 @@ pub fn RegisterManager( |
| 146 | comptime count: comptime_int, | 146 | comptime count: comptime_int, |
| 147 | insts: [count]?Air.Inst.Index, | 147 | insts: [count]?Air.Inst.Index, |
| 148 | ) ?[count]Register { | 148 | ) ?[count]Register { |
| 149 | comptime assert(count > 0 and count <= callee_preserved_regs.len); | 149 | comptime assert(count > 0 and count <= tracked_registers.len); |
| 150 | 150 | ||
| 151 | const free_registers = @popCount(FreeRegInt, self.free_registers); | 151 | const free_registers = @popCount(FreeRegInt, self.free_registers); |
| 152 | if (free_registers < count) return null; | 152 | if (free_registers < count) return null; |
| 153 | 153 | ||
| 154 | var regs: [count]Register = undefined; | 154 | var regs: [count]Register = undefined; |
| 155 | var i: usize = 0; | 155 | var i: usize = 0; |
| 156 | for (callee_preserved_regs) |reg| { | 156 | for (tracked_registers) |reg| { |
| 157 | if (i >= count) break; | 157 | if (i >= count) break; |
| 158 | if (self.isRegFrozen(reg)) continue; | 158 | if (self.isRegFrozen(reg)) continue; |
| 159 | if (self.isRegFree(reg)) { | 159 | if (self.isRegFree(reg)) { |
| ... | @@ -192,8 +192,8 @@ pub fn RegisterManager( | ... | @@ -192,8 +192,8 @@ pub fn RegisterManager( |
| 192 | comptime count: comptime_int, | 192 | comptime count: comptime_int, |
| 193 | insts: [count]?Air.Inst.Index, | 193 | insts: [count]?Air.Inst.Index, |
| 194 | ) AllocateRegistersError![count]Register { | 194 | ) AllocateRegistersError![count]Register { |
| 195 | comptime assert(count > 0 and count <= callee_preserved_regs.len); | 195 | comptime assert(count > 0 and count <= tracked_registers.len); |
| 196 | if (count > callee_preserved_regs.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters; | 196 | if (count > tracked_registers.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters; |
| 197 | 197 | ||
| 198 | const result = self.tryAllocRegs(count, insts) orelse blk: { | 198 | const result = self.tryAllocRegs(count, insts) orelse blk: { |
| 199 | // We'll take over the first count registers. Spill | 199 | // We'll take over the first count registers. Spill |
| ... | @@ -201,7 +201,7 @@ pub fn RegisterManager( | ... | @@ -201,7 +201,7 @@ pub fn RegisterManager( |
| 201 | // stack allocations. | 201 | // stack allocations. |
| 202 | var regs: [count]Register = undefined; | 202 | var regs: [count]Register = undefined; |
| 203 | var i: usize = 0; | 203 | var i: usize = 0; |
| 204 | for (callee_preserved_regs) |reg| { | 204 | for (tracked_registers) |reg| { |
| 205 | if (i >= count) break; | 205 | if (i >= count) break; |
| 206 | if (self.isRegFrozen(reg)) continue; | 206 | if (self.isRegFrozen(reg)) continue; |
| 207 | 207 | ||
| ... | @@ -247,6 +247,7 @@ pub fn RegisterManager( | ... | @@ -247,6 +247,7 @@ pub fn RegisterManager( |
| 247 | /// register. | 247 | /// register. |
| 248 | pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void { | 248 | pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void { |
| 249 | const index = indexOfRegIntoTracked(reg) orelse return; | 249 | const index = indexOfRegIntoTracked(reg) orelse return; |
| 250 | log.debug("getReg {} for inst {}", .{ reg, inst }); | ||
| 250 | self.markRegAllocated(reg); | 251 | self.markRegAllocated(reg); |
| 251 | 252 | ||
| 252 | if (inst) |tracked_inst| | 253 | if (inst) |tracked_inst| |
| ... | @@ -275,6 +276,7 @@ pub fn RegisterManager( | ... | @@ -275,6 +276,7 @@ pub fn RegisterManager( |
| 275 | /// spilling is necessary. | 276 | /// spilling is necessary. |
| 276 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void { | 277 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void { |
| 277 | const index = indexOfRegIntoTracked(reg) orelse return; | 278 | const index = indexOfRegIntoTracked(reg) orelse return; |
| 279 | log.debug("getRegAssumeFree {} for inst {}", .{ reg, inst }); | ||
| 278 | self.markRegAllocated(reg); | 280 | self.markRegAllocated(reg); |
| 279 | 281 | ||
| 280 | assert(self.isRegFree(reg)); | 282 | assert(self.isRegFree(reg)); |
| ... | @@ -303,7 +305,7 @@ const MockRegister1 = enum(u2) { | ... | @@ -303,7 +305,7 @@ const MockRegister1 = enum(u2) { |
| 303 | return @enumToInt(reg); | 305 | return @enumToInt(reg); |
| 304 | } | 306 | } |
| 305 | 307 | ||
| 306 | const callee_preserved_regs = [_]MockRegister1{ .r2, .r3 }; | 308 | const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; |
| 307 | }; | 309 | }; |
| 308 | 310 | ||
| 309 | const MockRegister2 = enum(u2) { | 311 | const MockRegister2 = enum(u2) { |
| ... | @@ -316,13 +318,13 @@ const MockRegister2 = enum(u2) { | ... | @@ -316,13 +318,13 @@ const MockRegister2 = enum(u2) { |
| 316 | return @enumToInt(reg); | 318 | return @enumToInt(reg); |
| 317 | } | 319 | } |
| 318 | 320 | ||
| 319 | const callee_preserved_regs = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; | 321 | const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; |
| 320 | }; | 322 | }; |
| 321 | 323 | ||
| 322 | fn MockFunction(comptime Register: type) type { | 324 | fn MockFunction(comptime Register: type) type { |
| 323 | return struct { | 325 | return struct { |
| 324 | allocator: Allocator, | 326 | allocator: Allocator, |
| 325 | register_manager: RegisterManager(Self, Register, &Register.callee_preserved_regs) = .{}, | 327 | register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, |
| 326 | spilled: std.ArrayListUnmanaged(Register) = .{}, | 328 | spilled: std.ArrayListUnmanaged(Register) = .{}, |
| 327 | 329 | ||
| 328 | const Self = @This(); | 330 | const Self = @This(); |