| 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 | 22 | const log = std.log.scoped(.codegen); |
| 23 | 23 | const build_options = @import("build_options"); |
| 24 | 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 | 27 | const FnResult = @import("../../codegen.zig").FnResult; |
| 28 | 28 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| ... | ... | @@ -34,6 +34,8 @@ const Register = bits.Register; |
| 34 | 34 | const Instruction = bits.Instruction; |
| 35 | 35 | const Condition = bits.Condition; |
| 36 | 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 | 39 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; |
| 38 | 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 | 790 | if (!elem_ty.hasRuntimeBits()) { |
| 789 | 791 | // As this stack item will never be dereferenced at runtime, |
| 790 | 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 | 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 | 1567 | |
| 1570 | 1568 | switch (mcv) { |
| 1571 | 1569 | .register => |reg| { |
| 1572 | // If it's in the registers table, need to associate the register with the | |
| 1573 | // new instruction. | |
| 1574 | if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { | |
| 1575 | if (!self.register_manager.isRegFree(reg)) { | |
| 1576 | self.register_manager.registers[index] = inst; | |
| 1577 | } | |
| 1570 | // We assert that this register is allocatable by asking | |
| 1571 | // for its index | |
| 1572 | const index = RegisterManager.indexOfRegIntoTracked(reg).?; // see note above | |
| 1573 | if (!self.register_manager.isRegFree(reg)) { | |
| 1574 | self.register_manager.registers[index] = inst; | |
| 1578 | 1575 | } |
| 1576 | ||
| 1579 | 1577 | log.debug("%{d} => {} (reused)", .{ inst, reg }); |
| 1580 | 1578 | }, |
| 1581 | 1579 | .stack_offset => |off| { |
| ... | ... | @@ -2545,13 +2543,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2545 | 2543 | // Architecture, compare flags are not preserved across |
| 2546 | 2544 | // calls. Therefore, if some value is currently stored there, we |
| 2547 | 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 | 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 | 2555 | if (info.return_value == .stack_offset) { |
| 2556 | log.debug("airCall: return by reference", .{}); | |
| 2555 | 2557 | const ret_ty = fn_ty.fnReturnType(); |
| 2556 | 2558 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 2557 | 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 | 2564 | .data = ret_ty, |
| 2563 | 2565 | }; |
| 2564 | 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 | 2568 | try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset }); |
| 2567 | 2569 | |
| 2568 | 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 | 2664 | const result: MCValue = result: { |
| 2663 | 2665 | switch (info.return_value) { |
| 2664 | 2666 | .register => |reg| { |
| 2665 | if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { | |
| 2666 | // Save function return value in a callee saved register | |
| 2667 | if (RegisterManager.indexOfRegIntoTracked(reg) == null) { | |
| 2668 | // Save function return value into a tracked register | |
| 2669 | log.debug("airCall: copying {} as it is not tracked", .{reg}); | |
| 2667 | 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 | 2 | const Register = bits.Register; |
| 3 | 3 | |
| 4 | 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 | 8 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; |
| 6 | 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 | 26 | pub fn RegisterManager( |
| 27 | 27 | comptime Function: type, |
| 28 | 28 | comptime Register: type, |
| 29 | comptime callee_preserved_regs: []const Register, | |
| 29 | comptime tracked_registers: []const Register, | |
| 30 | 30 | ) type { |
| 31 | 31 | // architectures which do not have a concept of registers should |
| 32 | 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 | 35 | return struct { |
| 36 | 36 | /// Tracks the AIR instruction allocated to every register. If |
| ... | ... | @@ -38,7 +38,7 @@ pub fn RegisterManager( |
| 38 | 38 | /// register is free), the value in that slot is undefined. |
| 39 | 39 | /// |
| 40 | 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 | 42 | /// Tracks which registers are free (in which case the |
| 43 | 43 | /// corresponding bit is set to 1) |
| 44 | 44 | free_registers: FreeRegInt = math.maxInt(FreeRegInt), |
| ... | ... | @@ -53,7 +53,7 @@ pub fn RegisterManager( |
| 53 | 53 | |
| 54 | 54 | /// An integer whose bits represent all the registers and |
| 55 | 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 | 57 | const ShiftInt = math.Log2Int(FreeRegInt); |
| 58 | 58 | |
| 59 | 59 | fn getFunction(self: *Self) *Function { |
| ... | ... | @@ -83,14 +83,14 @@ pub fn RegisterManager( |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 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 | 87 | if (reg.id() == cpreg.id()) return i; |
| 88 | 88 | } |
| 89 | 89 | return null; |
| 90 | 90 | } |
| 91 | 91 | |
| 92 | 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 | 96 | /// Returns true when this register is not tracked |
| ... | ... | @@ -146,14 +146,14 @@ pub fn RegisterManager( |
| 146 | 146 | comptime count: comptime_int, |
| 147 | 147 | insts: [count]?Air.Inst.Index, |
| 148 | 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 | 151 | const free_registers = @popCount(FreeRegInt, self.free_registers); |
| 152 | 152 | if (free_registers < count) return null; |
| 153 | 153 | |
| 154 | 154 | var regs: [count]Register = undefined; |
| 155 | 155 | var i: usize = 0; |
| 156 | for (callee_preserved_regs) |reg| { | |
| 156 | for (tracked_registers) |reg| { | |
| 157 | 157 | if (i >= count) break; |
| 158 | 158 | if (self.isRegFrozen(reg)) continue; |
| 159 | 159 | if (self.isRegFree(reg)) { |
| ... | ... | @@ -192,8 +192,8 @@ pub fn RegisterManager( |
| 192 | 192 | comptime count: comptime_int, |
| 193 | 193 | insts: [count]?Air.Inst.Index, |
| 194 | 194 | ) AllocateRegistersError![count]Register { |
| 195 | comptime assert(count > 0 and count <= callee_preserved_regs.len); | |
| 196 | if (count > callee_preserved_regs.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters; | |
| 195 | comptime assert(count > 0 and count <= tracked_registers.len); | |
| 196 | if (count > tracked_registers.len - @popCount(FreeRegInt, self.frozen_registers)) return error.OutOfRegisters; | |
| 197 | 197 | |
| 198 | 198 | const result = self.tryAllocRegs(count, insts) orelse blk: { |
| 199 | 199 | // We'll take over the first count registers. Spill |
| ... | ... | @@ -201,7 +201,7 @@ pub fn RegisterManager( |
| 201 | 201 | // stack allocations. |
| 202 | 202 | var regs: [count]Register = undefined; |
| 203 | 203 | var i: usize = 0; |
| 204 | for (callee_preserved_regs) |reg| { | |
| 204 | for (tracked_registers) |reg| { | |
| 205 | 205 | if (i >= count) break; |
| 206 | 206 | if (self.isRegFrozen(reg)) continue; |
| 207 | 207 | |
| ... | ... | @@ -247,6 +247,7 @@ pub fn RegisterManager( |
| 247 | 247 | /// register. |
| 248 | 248 | pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void { |
| 249 | 249 | const index = indexOfRegIntoTracked(reg) orelse return; |
| 250 | log.debug("getReg {} for inst {}", .{ reg, inst }); | |
| 250 | 251 | self.markRegAllocated(reg); |
| 251 | 252 | |
| 252 | 253 | if (inst) |tracked_inst| |
| ... | ... | @@ -275,6 +276,7 @@ pub fn RegisterManager( |
| 275 | 276 | /// spilling is necessary. |
| 276 | 277 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void { |
| 277 | 278 | const index = indexOfRegIntoTracked(reg) orelse return; |
| 279 | log.debug("getRegAssumeFree {} for inst {}", .{ reg, inst }); | |
| 278 | 280 | self.markRegAllocated(reg); |
| 279 | 281 | |
| 280 | 282 | assert(self.isRegFree(reg)); |
| ... | ... | @@ -303,7 +305,7 @@ const MockRegister1 = enum(u2) { |
| 303 | 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 | 311 | const MockRegister2 = enum(u2) { |
| ... | ... | @@ -316,13 +318,13 @@ const MockRegister2 = enum(u2) { |
| 316 | 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 | 324 | fn MockFunction(comptime Register: type) type { |
| 323 | 325 | return struct { |
| 324 | 326 | allocator: Allocator, |
| 325 | register_manager: RegisterManager(Self, Register, &Register.callee_preserved_regs) = .{}, | |
| 327 | register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, | |
| 326 | 328 | spilled: std.ArrayListUnmanaged(Register) = .{}, |
| 327 | 329 | |
| 328 | 330 | const Self = @This(); |