| author | |
| committer | |
| log | 5fbae9cd6fe7a53d85ad6dd20cab75f7b835f0ad |
| tree | ba874ceb64ee6c2de89223dbf87d4b0e0018d78f |
| parent | 078037ab9b410fa13a86eabdfc30918fc83cdcf3 |
| parent | 4590e980f77f406158258c8f44a76e28d53eff2f |
| signature |
stage2 ARM: implement caller-preserved registers17 files changed, 156 insertions(+), 177 deletions(-)
CMakeLists.txt+4| ... | @@ -597,14 +597,17 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -597,14 +597,17 @@ set(ZIG_STAGE2_SOURCES |
| 597 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig" | 597 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig" |
| 598 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Mir.zig" | 598 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Mir.zig" |
| 599 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/bits.zig" | 599 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/bits.zig" |
| 600 | "${CMAKE_SOURCE_DIR}/src/arch/aarch64/abi.zig" | ||
| 600 | "${CMAKE_SOURCE_DIR}/src/arch/arm/CodeGen.zig" | 601 | "${CMAKE_SOURCE_DIR}/src/arch/arm/CodeGen.zig" |
| 601 | "${CMAKE_SOURCE_DIR}/src/arch/arm/Emit.zig" | 602 | "${CMAKE_SOURCE_DIR}/src/arch/arm/Emit.zig" |
| 602 | "${CMAKE_SOURCE_DIR}/src/arch/arm/Mir.zig" | 603 | "${CMAKE_SOURCE_DIR}/src/arch/arm/Mir.zig" |
| 603 | "${CMAKE_SOURCE_DIR}/src/arch/arm/bits.zig" | 604 | "${CMAKE_SOURCE_DIR}/src/arch/arm/bits.zig" |
| 605 | "${CMAKE_SOURCE_DIR}/src/arch/arm/abi.zig" | ||
| 604 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/CodeGen.zig" | 606 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/CodeGen.zig" |
| 605 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/Emit.zig" | 607 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/Emit.zig" |
| 606 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/Mir.zig" | 608 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/Mir.zig" |
| 607 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/bits.zig" | 609 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/bits.zig" |
| 610 | "${CMAKE_SOURCE_DIR}/src/arch/riscv64/abi.zig" | ||
| 608 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/CodeGen.zig" | 611 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/CodeGen.zig" |
| 609 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Emit.zig" | 612 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Emit.zig" |
| 610 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig" | 613 | "${CMAKE_SOURCE_DIR}/src/arch/wasm/Mir.zig" |
| ... | @@ -612,6 +615,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -612,6 +615,7 @@ set(ZIG_STAGE2_SOURCES |
| 612 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig" | 615 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Emit.zig" |
| 613 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig" | 616 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/Mir.zig" |
| 614 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig" | 617 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/bits.zig" |
| 618 | "${CMAKE_SOURCE_DIR}/src/arch/x86_64/abi.zig" | ||
| 615 | "${CMAKE_SOURCE_DIR}/src/clang.zig" | 619 | "${CMAKE_SOURCE_DIR}/src/clang.zig" |
| 616 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" | 620 | "${CMAKE_SOURCE_DIR}/src/clang_options.zig" |
| 617 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" | 621 | "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" |
src/arch/aarch64/CodeGen.zig+13-10| ... | @@ -21,12 +21,21 @@ const DW = std.dwarf; | ... | @@ -21,12 +21,21 @@ const DW = std.dwarf; |
| 21 | const leb128 = std.leb; | 21 | 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 RegisterManager = @import("../../register_manager.zig").RegisterManager; | 24 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; |
| 25 | const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); | ||
| 25 | 26 | ||
| 26 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | 27 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 27 | const FnResult = @import("../../codegen.zig").FnResult; | 28 | const FnResult = @import("../../codegen.zig").FnResult; |
| 28 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 29 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 29 | 30 | ||
| 31 | const bits = @import("bits.zig"); | ||
| 32 | const abi = @import("abi.zig"); | ||
| 33 | const Register = bits.Register; | ||
| 34 | const Instruction = bits.Instruction; | ||
| 35 | const callee_preserved_regs = abi.callee_preserved_regs; | ||
| 36 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; | ||
| 37 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | ||
| 38 | |||
| 30 | const InnerError = error{ | 39 | const InnerError = error{ |
| 31 | OutOfMemory, | 40 | OutOfMemory, |
| 32 | CodegenFail, | 41 | CodegenFail, |
| ... | @@ -73,7 +82,7 @@ branch_stack: *std.ArrayList(Branch), | ... | @@ -73,7 +82,7 @@ branch_stack: *std.ArrayList(Branch), |
| 73 | // Key is the block instruction | 82 | // Key is the block instruction |
| 74 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, | 83 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 75 | 84 | ||
| 76 | register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{}, | 85 | register_manager: RegisterManager = .{}, |
| 77 | /// Maps offset to what is stored there. | 86 | /// Maps offset to what is stored there. |
| 78 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, | 87 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 79 | 88 | ||
| ... | @@ -1836,7 +1845,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind | ... | @@ -1836,7 +1845,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind |
| 1836 | .register => |reg| { | 1845 | .register => |reg| { |
| 1837 | // If it's in the registers table, need to associate the register with the | 1846 | // If it's in the registers table, need to associate the register with the |
| 1838 | // new instruction. | 1847 | // new instruction. |
| 1839 | if (reg.allocIndex()) |index| { | 1848 | if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { |
| 1840 | if (!self.register_manager.isRegFree(reg)) { | 1849 | if (!self.register_manager.isRegFree(reg)) { |
| 1841 | self.register_manager.registers[index] = inst; | 1850 | self.register_manager.registers[index] = inst; |
| 1842 | } | 1851 | } |
| ... | @@ -2475,7 +2484,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2475,7 +2484,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2475 | const result: MCValue = result: { | 2484 | const result: MCValue = result: { |
| 2476 | switch (info.return_value) { | 2485 | switch (info.return_value) { |
| 2477 | .register => |reg| { | 2486 | .register => |reg| { |
| 2478 | if (Register.allocIndex(reg) == null) { | 2487 | if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { |
| 2479 | // Save function return value in a callee saved register | 2488 | // Save function return value in a callee saved register |
| 2480 | break :result try self.copyToNewRegister(inst, info.return_value); | 2489 | break :result try self.copyToNewRegister(inst, info.return_value); |
| 2481 | } | 2490 | } |
| ... | @@ -4017,12 +4026,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro | ... | @@ -4017,12 +4026,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro |
| 4017 | return error.CodegenFail; | 4026 | return error.CodegenFail; |
| 4018 | } | 4027 | } |
| 4019 | 4028 | ||
| 4020 | const Register = @import("bits.zig").Register; | ||
| 4021 | const Instruction = @import("bits.zig").Instruction; | ||
| 4022 | const callee_preserved_regs = @import("bits.zig").callee_preserved_regs; | ||
| 4023 | const c_abi_int_param_regs = @import("bits.zig").c_abi_int_param_regs; | ||
| 4024 | const c_abi_int_return_regs = @import("bits.zig").c_abi_int_return_regs; | ||
| 4025 | |||
| 4026 | fn parseRegName(name: []const u8) ?Register { | 4029 | fn parseRegName(name: []const u8) ?Register { |
| 4027 | if (@hasDecl(Register, "parseRegName")) { | 4030 | if (@hasDecl(Register, "parseRegName")) { |
| 4028 | return Register.parseRegName(name); | 4031 | return Register.parseRegName(name); |
src/arch/aarch64/abi.zig created+20| ... | @@ -0,0 +1,20 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const bits = @import("bits.zig"); | ||
| 3 | const Register = bits.Register; | ||
| 4 | |||
| 5 | const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct { | ||
| 6 | pub const callee_preserved_regs = [_]Register{ | ||
| 7 | .x20, .x21, .x22, .x23, | ||
| 8 | .x24, .x25, .x26, .x27, | ||
| 9 | .x28, | ||
| 10 | }; | ||
| 11 | } else struct { | ||
| 12 | pub const callee_preserved_regs = [_]Register{ | ||
| 13 | .x19, .x20, .x21, .x22, .x23, | ||
| 14 | .x24, .x25, .x26, .x27, .x28, | ||
| 15 | }; | ||
| 16 | }; | ||
| 17 | pub const callee_preserved_regs = callee_preserved_regs_impl.callee_preserved_regs; | ||
| 18 | |||
| 19 | pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | ||
| 20 | pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | ||
src/arch/aarch64/bits.zig-25| ... | @@ -72,14 +72,6 @@ pub const Register = enum(u7) { | ... | @@ -72,14 +72,6 @@ pub const Register = enum(u7) { |
| 72 | }; | 72 | }; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | /// Returns the index into `callee_preserved_regs`. | ||
| 76 | pub fn allocIndex(self: Register) ?u4 { | ||
| 77 | inline for (callee_preserved_regs) |cpreg, i| { | ||
| 78 | if (self.id() == cpreg.id()) return i; | ||
| 79 | } | ||
| 80 | return null; | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn dwarfLocOp(self: Register) u8 { | 75 | pub fn dwarfLocOp(self: Register) u8 { |
| 84 | return @as(u8, self.enc()) + DW.OP.reg0; | 76 | return @as(u8, self.enc()) + DW.OP.reg0; |
| 85 | } | 77 | } |
| ... | @@ -87,23 +79,6 @@ pub const Register = enum(u7) { | ... | @@ -87,23 +79,6 @@ pub const Register = enum(u7) { |
| 87 | 79 | ||
| 88 | // zig fmt: on | 80 | // zig fmt: on |
| 89 | 81 | ||
| 90 | const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct { | ||
| 91 | pub const callee_preserved_regs = [_]Register{ | ||
| 92 | .x20, .x21, .x22, .x23, | ||
| 93 | .x24, .x25, .x26, .x27, | ||
| 94 | .x28, | ||
| 95 | }; | ||
| 96 | } else struct { | ||
| 97 | pub const callee_preserved_regs = [_]Register{ | ||
| 98 | .x19, .x20, .x21, .x22, .x23, | ||
| 99 | .x24, .x25, .x26, .x27, .x28, | ||
| 100 | }; | ||
| 101 | }; | ||
| 102 | pub const callee_preserved_regs = callee_preserved_regs_impl.callee_preserved_regs; | ||
| 103 | |||
| 104 | pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | ||
| 105 | pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; | ||
| 106 | |||
| 107 | test "Register.enc" { | 82 | test "Register.enc" { |
| 108 | try testing.expectEqual(@as(u5, 0), Register.x0.enc()); | 83 | try testing.expectEqual(@as(u5, 0), Register.x0.enc()); |
| 109 | try testing.expectEqual(@as(u5, 0), Register.w0.enc()); | 84 | try testing.expectEqual(@as(u5, 0), Register.w0.enc()); |
src/arch/arm/CodeGen.zig+32-26| ... | @@ -21,12 +21,24 @@ const DW = std.dwarf; | ... | @@ -21,12 +21,24 @@ const DW = std.dwarf; |
| 21 | const leb128 = std.leb; | 21 | 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 RegisterManager = @import("../../register_manager.zig").RegisterManager; | 24 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; |
| 25 | const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers); | ||
| 25 | 26 | ||
| 26 | const FnResult = @import("../../codegen.zig").FnResult; | 27 | const FnResult = @import("../../codegen.zig").FnResult; |
| 27 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | 28 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 28 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 29 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 29 | 30 | ||
| 31 | const bits = @import("bits.zig"); | ||
| 32 | const abi = @import("abi.zig"); | ||
| 33 | const Register = bits.Register; | ||
| 34 | const Instruction = bits.Instruction; | ||
| 35 | const Condition = bits.Condition; | ||
| 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; | ||
| 39 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; | ||
| 40 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | ||
| 41 | |||
| 30 | const InnerError = error{ | 42 | const InnerError = error{ |
| 31 | OutOfMemory, | 43 | OutOfMemory, |
| 32 | CodegenFail, | 44 | CodegenFail, |
| ... | @@ -73,7 +85,7 @@ branch_stack: *std.ArrayList(Branch), | ... | @@ -73,7 +85,7 @@ branch_stack: *std.ArrayList(Branch), |
| 73 | // Key is the block instruction | 85 | // Key is the block instruction |
| 74 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, | 86 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 75 | 87 | ||
| 76 | register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{}, | 88 | register_manager: RegisterManager = .{}, |
| 77 | /// Maps offset to what is stored there. | 89 | /// Maps offset to what is stored there. |
| 78 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, | 90 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 79 | /// Tracks the current instruction allocated to the compare flags | 91 | /// Tracks the current instruction allocated to the compare flags |
| ... | @@ -778,10 +790,6 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { | ... | @@ -778,10 +790,6 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 778 | if (!elem_ty.hasRuntimeBits()) { | 790 | if (!elem_ty.hasRuntimeBits()) { |
| 779 | // As this stack item will never be dereferenced at runtime, | 791 | // As this stack item will never be dereferenced at runtime, |
| 780 | // return the current stack offset | 792 | // return the current stack offset |
| 781 | try self.stack.putNoClobber(self.gpa, self.next_stack_offset, .{ | ||
| 782 | .inst = inst, | ||
| 783 | .size = 0, | ||
| 784 | }); | ||
| 785 | return self.next_stack_offset; | 793 | return self.next_stack_offset; |
| 786 | } | 794 | } |
| 787 | 795 | ||
| ... | @@ -1559,13 +1567,13 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind | ... | @@ -1559,13 +1567,13 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind |
| 1559 | 1567 | ||
| 1560 | switch (mcv) { | 1568 | switch (mcv) { |
| 1561 | .register => |reg| { | 1569 | .register => |reg| { |
| 1562 | // If it's in the registers table, need to associate the register with the | 1570 | // We assert that this register is allocatable by asking |
| 1563 | // new instruction. | 1571 | // for its index |
| 1564 | if (reg.allocIndex()) |index| { | 1572 | const index = RegisterManager.indexOfRegIntoTracked(reg).?; // see note above |
| 1565 | if (!self.register_manager.isRegFree(reg)) { | 1573 | if (!self.register_manager.isRegFree(reg)) { |
| 1566 | self.register_manager.registers[index] = inst; | 1574 | self.register_manager.registers[index] = inst; |
| 1567 | } | ||
| 1568 | } | 1575 | } |
| 1576 | |||
| 1569 | log.debug("%{d} => {} (reused)", .{ inst, reg }); | 1577 | log.debug("%{d} => {} (reused)", .{ inst, reg }); |
| 1570 | }, | 1578 | }, |
| 1571 | .stack_offset => |off| { | 1579 | .stack_offset => |off| { |
| ... | @@ -2535,13 +2543,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2535,13 +2543,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2535 | // Architecture, compare flags are not preserved across | 2543 | // Architecture, compare flags are not preserved across |
| 2536 | // calls. Therefore, if some value is currently stored there, we | 2544 | // calls. Therefore, if some value is currently stored there, we |
| 2537 | // need to save it. | 2545 | // need to save it. |
| 2538 | // | ||
| 2539 | // TODO once caller-saved registers are implemented, save them | ||
| 2540 | // here too, but crucially *after* we save the compare flags as | ||
| 2541 | // saving compare flags may require a new caller-saved register | ||
| 2542 | try self.spillCompareFlagsIfOccupied(); | 2546 | try self.spillCompareFlagsIfOccupied(); |
| 2543 | 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 | |||
| 2544 | if (info.return_value == .stack_offset) { | 2555 | if (info.return_value == .stack_offset) { |
| 2556 | log.debug("airCall: return by reference", .{}); | ||
| 2545 | const ret_ty = fn_ty.fnReturnType(); | 2557 | const ret_ty = fn_ty.fnReturnType(); |
| 2546 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | 2558 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 2547 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); | 2559 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); |
| ... | @@ -2552,7 +2564,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2552,7 +2564,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2552 | .data = ret_ty, | 2564 | .data = ret_ty, |
| 2553 | }; | 2565 | }; |
| 2554 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); | 2566 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| 2555 | try self.register_manager.getReg(.r0, inst); | 2567 | try self.register_manager.getReg(.r0, null); |
| 2556 | try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset }); | 2568 | try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset }); |
| 2557 | 2569 | ||
| 2558 | info.return_value = .{ .stack_offset = stack_offset }; | 2570 | info.return_value = .{ .stack_offset = stack_offset }; |
| ... | @@ -2652,8 +2664,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2652,8 +2664,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2652 | const result: MCValue = result: { | 2664 | const result: MCValue = result: { |
| 2653 | switch (info.return_value) { | 2665 | switch (info.return_value) { |
| 2654 | .register => |reg| { | 2666 | .register => |reg| { |
| 2655 | if (Register.allocIndex(reg) == null) { | 2667 | if (RegisterManager.indexOfRegIntoTracked(reg) == null) { |
| 2656 | // 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}); | ||
| 2657 | break :result try self.copyToNewRegister(inst, info.return_value); | 2670 | break :result try self.copyToNewRegister(inst, info.return_value); |
| 2658 | } | 2671 | } |
| 2659 | }, | 2672 | }, |
| ... | @@ -4495,13 +4508,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro | ... | @@ -4495,13 +4508,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro |
| 4495 | return error.CodegenFail; | 4508 | return error.CodegenFail; |
| 4496 | } | 4509 | } |
| 4497 | 4510 | ||
| 4498 | const Register = @import("bits.zig").Register; | ||
| 4499 | const Instruction = @import("bits.zig").Instruction; | ||
| 4500 | const Condition = @import("bits.zig").Condition; | ||
| 4501 | const callee_preserved_regs = @import("bits.zig").callee_preserved_regs; | ||
| 4502 | const c_abi_int_param_regs = @import("bits.zig").c_abi_int_param_regs; | ||
| 4503 | const c_abi_int_return_regs = @import("bits.zig").c_abi_int_return_regs; | ||
| 4504 | |||
| 4505 | fn parseRegName(name: []const u8) ?Register { | 4511 | fn parseRegName(name: []const u8) ?Register { |
| 4506 | if (@hasDecl(Register, "parseRegName")) { | 4512 | if (@hasDecl(Register, "parseRegName")) { |
| 4507 | return Register.parseRegName(name); | 4513 | return Register.parseRegName(name); |
src/arch/arm/abi.zig created+9| ... | @@ -0,0 +1,9 @@ | ||
| 1 | const bits = @import("bits.zig"); | ||
| 2 | const Register = bits.Register; | ||
| 3 | |||
| 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 | |||
| 8 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | ||
| 9 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | ||
src/arch/arm/bits.zig-12| ... | @@ -162,14 +162,6 @@ pub const Register = enum(u5) { | ... | @@ -162,14 +162,6 @@ pub const Register = enum(u5) { |
| 162 | return @truncate(u4, @enumToInt(self)); | 162 | return @truncate(u4, @enumToInt(self)); |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | /// Returns the index into `callee_preserved_regs`. | ||
| 166 | pub fn allocIndex(self: Register) ?u4 { | ||
| 167 | inline for (callee_preserved_regs) |cpreg, i| { | ||
| 168 | if (self.id() == cpreg.id()) return i; | ||
| 169 | } | ||
| 170 | return null; | ||
| 171 | } | ||
| 172 | |||
| 173 | pub fn dwarfLocOp(self: Register) u8 { | 165 | pub fn dwarfLocOp(self: Register) u8 { |
| 174 | return @as(u8, self.id()) + DW.OP.reg0; | 166 | return @as(u8, self.id()) + DW.OP.reg0; |
| 175 | } | 167 | } |
| ... | @@ -187,10 +179,6 @@ pub const Psr = enum { | ... | @@ -187,10 +179,6 @@ pub const Psr = enum { |
| 187 | spsr, | 179 | spsr, |
| 188 | }; | 180 | }; |
| 189 | 181 | ||
| 190 | pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 }; | ||
| 191 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | ||
| 192 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | ||
| 193 | |||
| 194 | /// Represents an instruction in the ARM instruction set architecture | 182 | /// Represents an instruction in the ARM instruction set architecture |
| 195 | pub const Instruction = union(enum) { | 183 | pub const Instruction = union(enum) { |
| 196 | data_processing: packed struct { | 184 | data_processing: packed struct { |
src/arch/riscv64/CodeGen.zig+11-8| ... | @@ -21,12 +21,19 @@ const DW = std.dwarf; | ... | @@ -21,12 +21,19 @@ const DW = std.dwarf; |
| 21 | const leb128 = std.leb; | 21 | 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 RegisterManager = @import("../../register_manager.zig").RegisterManager; | 24 | const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; |
| 25 | const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); | ||
| 25 | 26 | ||
| 26 | const FnResult = @import("../../codegen.zig").FnResult; | 27 | const FnResult = @import("../../codegen.zig").FnResult; |
| 27 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | 28 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 28 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 29 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 29 | 30 | ||
| 31 | const bits = @import("bits.zig"); | ||
| 32 | const abi = @import("abi.zig"); | ||
| 33 | const Register = bits.Register; | ||
| 34 | const Instruction = abi.Instruction; | ||
| 35 | const callee_preserved_regs = abi.callee_preserved_regs; | ||
| 36 | |||
| 30 | const InnerError = error{ | 37 | const InnerError = error{ |
| 31 | OutOfMemory, | 38 | OutOfMemory, |
| 32 | CodegenFail, | 39 | CodegenFail, |
| ... | @@ -75,7 +82,7 @@ branch_stack: *std.ArrayList(Branch), | ... | @@ -75,7 +82,7 @@ branch_stack: *std.ArrayList(Branch), |
| 75 | // Key is the block instruction | 82 | // Key is the block instruction |
| 76 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, | 83 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 77 | 84 | ||
| 78 | register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{}, | 85 | register_manager: RegisterManager = .{}, |
| 79 | /// Maps offset to what is stored there. | 86 | /// Maps offset to what is stored there. |
| 80 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, | 87 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 81 | 88 | ||
| ... | @@ -1230,7 +1237,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind | ... | @@ -1230,7 +1237,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind |
| 1230 | .register => |reg| { | 1237 | .register => |reg| { |
| 1231 | // If it's in the registers table, need to associate the register with the | 1238 | // If it's in the registers table, need to associate the register with the |
| 1232 | // new instruction. | 1239 | // new instruction. |
| 1233 | if (reg.allocIndex()) |index| { | 1240 | if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { |
| 1234 | if (!self.register_manager.isRegFree(reg)) { | 1241 | if (!self.register_manager.isRegFree(reg)) { |
| 1235 | self.register_manager.registers[index] = inst; | 1242 | self.register_manager.registers[index] = inst; |
| 1236 | } | 1243 | } |
| ... | @@ -1545,7 +1552,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -1545,7 +1552,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1545 | const result: MCValue = result: { | 1552 | const result: MCValue = result: { |
| 1546 | switch (info.return_value) { | 1553 | switch (info.return_value) { |
| 1547 | .register => |reg| { | 1554 | .register => |reg| { |
| 1548 | if (Register.allocIndex(reg) == null) { | 1555 | if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { |
| 1549 | // Save function return value in a callee saved register | 1556 | // Save function return value in a callee saved register |
| 1550 | break :result try self.copyToNewRegister(inst, info.return_value); | 1557 | break :result try self.copyToNewRegister(inst, info.return_value); |
| 1551 | } | 1558 | } |
| ... | @@ -2549,10 +2556,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro | ... | @@ -2549,10 +2556,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro |
| 2549 | return error.CodegenFail; | 2556 | return error.CodegenFail; |
| 2550 | } | 2557 | } |
| 2551 | 2558 | ||
| 2552 | const Register = @import("bits.zig").Register; | ||
| 2553 | const Instruction = @import("bits.zig").Instruction; | ||
| 2554 | const callee_preserved_regs = @import("bits.zig").callee_preserved_regs; | ||
| 2555 | |||
| 2556 | fn parseRegName(name: []const u8) ?Register { | 2559 | fn parseRegName(name: []const u8) ?Register { |
| 2557 | if (@hasDecl(Register, "parseRegName")) { | 2560 | if (@hasDecl(Register, "parseRegName")) { |
| 2558 | return Register.parseRegName(name); | 2561 | return Register.parseRegName(name); |
src/arch/riscv64/abi.zig created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | const bits = @import("bits.zig"); | ||
| 2 | const Register = bits.Register; | ||
| 3 | |||
| 4 | pub const callee_preserved_regs = [_]Register{ | ||
| 5 | .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, | ||
| 6 | }; | ||
src/arch/riscv64/bits.zig-12| ... | @@ -409,14 +409,6 @@ pub const Register = enum(u6) { | ... | @@ -409,14 +409,6 @@ pub const Register = enum(u6) { |
| 409 | return @truncate(u5, @enumToInt(self)); | 409 | return @truncate(u5, @enumToInt(self)); |
| 410 | } | 410 | } |
| 411 | 411 | ||
| 412 | /// Returns the index into `callee_preserved_regs`. | ||
| 413 | pub fn allocIndex(self: Register) ?u4 { | ||
| 414 | inline for (callee_preserved_regs) |cpreg, i| { | ||
| 415 | if (self.id() == cpreg.id()) return i; | ||
| 416 | } | ||
| 417 | return null; | ||
| 418 | } | ||
| 419 | |||
| 420 | pub fn dwarfLocOp(reg: Register) u8 { | 412 | pub fn dwarfLocOp(reg: Register) u8 { |
| 421 | return @as(u8, reg.id()) + DW.OP.reg0; | 413 | return @as(u8, reg.id()) + DW.OP.reg0; |
| 422 | } | 414 | } |
| ... | @@ -424,10 +416,6 @@ pub const Register = enum(u6) { | ... | @@ -424,10 +416,6 @@ pub const Register = enum(u6) { |
| 424 | 416 | ||
| 425 | // zig fmt: on | 417 | // zig fmt: on |
| 426 | 418 | ||
| 427 | pub const callee_preserved_regs = [_]Register{ | ||
| 428 | .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, | ||
| 429 | }; | ||
| 430 | |||
| 431 | test "serialize instructions" { | 419 | test "serialize instructions" { |
| 432 | const Testcase = struct { | 420 | const Testcase = struct { |
| 433 | inst: Instruction, | 421 | inst: Instruction, |
src/arch/x86/bits.zig+4-17| ... | @@ -4,16 +4,16 @@ const DW = std.dwarf; | ... | @@ -4,16 +4,16 @@ const DW = std.dwarf; |
| 4 | // zig fmt: off | 4 | // zig fmt: off |
| 5 | pub const Register = enum(u8) { | 5 | pub const Register = enum(u8) { |
| 6 | // 0 through 7, 32-bit registers. id is int value | 6 | // 0 through 7, 32-bit registers. id is int value |
| 7 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | 7 | eax, ecx, edx, ebx, esp, ebp, esi, edi, |
| 8 | 8 | ||
| 9 | // 8-15, 16-bit registers. id is int value - 8. | 9 | // 8-15, 16-bit registers. id is int value - 8. |
| 10 | ax, cx, dx, bx, sp, bp, si, di, | 10 | ax, cx, dx, bx, sp, bp, si, di, |
| 11 | 11 | ||
| 12 | // 16-23, 8-bit registers. id is int value - 16. | 12 | // 16-23, 8-bit registers. id is int value - 16. |
| 13 | al, cl, dl, bl, ah, ch, dh, bh, | 13 | al, cl, dl, bl, ah, ch, dh, bh, |
| 14 | 14 | ||
| 15 | /// Returns the bit-width of the register. | 15 | /// Returns the bit-width of the register. |
| 16 | pub fn size(self: @This()) u7 { | 16 | pub fn size(self: Register) u7 { |
| 17 | return switch (@enumToInt(self)) { | 17 | return switch (@enumToInt(self)) { |
| 18 | 0...7 => 32, | 18 | 0...7 => 32, |
| 19 | 8...15 => 16, | 19 | 8...15 => 16, |
| ... | @@ -25,22 +25,10 @@ pub const Register = enum(u8) { | ... | @@ -25,22 +25,10 @@ pub const Register = enum(u8) { |
| 25 | /// Returns the register's id. This is used in practically every opcode the | 25 | /// Returns the register's id. This is used in practically every opcode the |
| 26 | /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move | 26 | /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move |
| 27 | /// instruction, and is used in the R/M byte. | 27 | /// instruction, and is used in the R/M byte. |
| 28 | pub fn id(self: @This()) u3 { | 28 | pub fn id(self: Register) u3 { |
| 29 | return @truncate(u3, @enumToInt(self)); | 29 | return @truncate(u3, @enumToInt(self)); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | /// Returns the index into `callee_preserved_regs`. | ||
| 33 | pub fn allocIndex(self: Register) ?u4 { | ||
| 34 | return switch (self) { | ||
| 35 | .eax, .ax, .al => 0, | ||
| 36 | .ecx, .cx, .cl => 1, | ||
| 37 | .edx, .dx, .dl => 2, | ||
| 38 | .esi, .si => 3, | ||
| 39 | .edi, .di => 4, | ||
| 40 | else => null, | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Convert from any register to its 32 bit alias. | 32 | /// Convert from any register to its 32 bit alias. |
| 45 | pub fn to32(self: Register) Register { | 33 | pub fn to32(self: Register) Register { |
| 46 | return @intToEnum(Register, @as(u8, self.id())); | 34 | return @intToEnum(Register, @as(u8, self.id())); |
| ... | @@ -56,7 +44,6 @@ pub const Register = enum(u8) { | ... | @@ -56,7 +44,6 @@ pub const Register = enum(u8) { |
| 56 | return @intToEnum(Register, @as(u8, self.id()) + 16); | 44 | return @intToEnum(Register, @as(u8, self.id()) + 16); |
| 57 | } | 45 | } |
| 58 | 46 | ||
| 59 | |||
| 60 | pub fn dwarfLocOp(reg: Register) u8 { | 47 | pub fn dwarfLocOp(reg: Register) u8 { |
| 61 | return switch (reg.to32()) { | 48 | return switch (reg.to32()) { |
| 62 | .eax => DW.OP.reg0, | 49 | .eax => DW.OP.reg0, |
src/arch/x86_64/CodeGen.zig+9-14| ... | @@ -27,6 +27,13 @@ const Type = @import("../../type.zig").Type; | ... | @@ -27,6 +27,13 @@ const Type = @import("../../type.zig").Type; |
| 27 | const TypedValue = @import("../../TypedValue.zig"); | 27 | const TypedValue = @import("../../TypedValue.zig"); |
| 28 | const Value = @import("../../value.zig").Value; | 28 | const Value = @import("../../value.zig").Value; |
| 29 | 29 | ||
| 30 | const bits = @import("bits.zig"); | ||
| 31 | const abi = @import("abi.zig"); | ||
| 32 | const Register = bits.Register; | ||
| 33 | const callee_preserved_regs = abi.callee_preserved_regs; | ||
| 34 | const c_abi_int_param_regs = abi.c_abi_int_param_regs; | ||
| 35 | const c_abi_int_return_regs = abi.c_abi_int_return_regs; | ||
| 36 | |||
| 30 | const InnerError = error{ | 37 | const InnerError = error{ |
| 31 | OutOfMemory, | 38 | OutOfMemory, |
| 32 | CodegenFail, | 39 | CodegenFail, |
| ... | @@ -2336,7 +2343,7 @@ fn reuseOperand( | ... | @@ -2336,7 +2343,7 @@ fn reuseOperand( |
| 2336 | .register => |reg| { | 2343 | .register => |reg| { |
| 2337 | // If it's in the registers table, need to associate the register with the | 2344 | // If it's in the registers table, need to associate the register with the |
| 2338 | // new instruction. | 2345 | // new instruction. |
| 2339 | if (reg.allocIndex()) |index| { | 2346 | if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { |
| 2340 | if (!self.register_manager.isRegFree(reg)) { | 2347 | if (!self.register_manager.isRegFree(reg)) { |
| 2341 | self.register_manager.registers[index] = inst; | 2348 | self.register_manager.registers[index] = inst; |
| 2342 | } | 2349 | } |
| ... | @@ -3483,7 +3490,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3483,7 +3490,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3483 | const result: MCValue = result: { | 3490 | const result: MCValue = result: { |
| 3484 | switch (info.return_value) { | 3491 | switch (info.return_value) { |
| 3485 | .register => |reg| { | 3492 | .register => |reg| { |
| 3486 | if (Register.allocIndex(reg) == null) { | 3493 | if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { |
| 3487 | // Save function return value in a callee saved register | 3494 | // Save function return value in a callee saved register |
| 3488 | break :result try self.copyToRegisterWithInstTracking( | 3495 | break :result try self.copyToRegisterWithInstTracking( |
| 3489 | inst, | 3496 | inst, |
| ... | @@ -5966,18 +5973,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro | ... | @@ -5966,18 +5973,6 @@ fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerErro |
| 5966 | return error.CodegenFail; | 5973 | return error.CodegenFail; |
| 5967 | } | 5974 | } |
| 5968 | 5975 | ||
| 5969 | const Register = @import("bits.zig").Register; | ||
| 5970 | |||
| 5971 | const Instruction = void; | ||
| 5972 | |||
| 5973 | const Condition = void; | ||
| 5974 | |||
| 5975 | const callee_preserved_regs = @import("bits.zig").callee_preserved_regs; | ||
| 5976 | |||
| 5977 | const c_abi_int_param_regs = @import("bits.zig").c_abi_int_param_regs; | ||
| 5978 | |||
| 5979 | const c_abi_int_return_regs = @import("bits.zig").c_abi_int_return_regs; | ||
| 5980 | |||
| 5981 | fn parseRegName(name: []const u8) ?Register { | 5976 | fn parseRegName(name: []const u8) ?Register { |
| 5982 | if (@hasDecl(Register, "parseRegName")) { | 5977 | if (@hasDecl(Register, "parseRegName")) { |
| 5983 | return Register.parseRegName(name); | 5978 | return Register.parseRegName(name); |
src/arch/x86_64/Emit.zig+2-1| ... | @@ -6,6 +6,7 @@ const Emit = @This(); | ... | @@ -6,6 +6,7 @@ const Emit = @This(); |
| 6 | const std = @import("std"); | 6 | const std = @import("std"); |
| 7 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| 8 | const bits = @import("bits.zig"); | 8 | const bits = @import("bits.zig"); |
| 9 | const abi = @import("abi.zig"); | ||
| 9 | const leb128 = std.leb; | 10 | const leb128 = std.leb; |
| 10 | const link = @import("../../link.zig"); | 11 | const link = @import("../../link.zig"); |
| 11 | const log = std.log.scoped(.codegen); | 12 | const log = std.log.scoped(.codegen); |
| ... | @@ -265,7 +266,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I | ... | @@ -265,7 +266,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(emit: *Emit, tag: Tag, inst: Mir.Inst.I |
| 265 | const data = emit.mir.extraData(Mir.RegsToPushOrPop, payload).data; | 266 | const data = emit.mir.extraData(Mir.RegsToPushOrPop, payload).data; |
| 266 | const regs = data.regs; | 267 | const regs = data.regs; |
| 267 | var disp: u32 = data.disp + 8; | 268 | var disp: u32 = data.disp + 8; |
| 268 | for (bits.callee_preserved_regs) |reg, i| { | 269 | for (abi.callee_preserved_regs) |reg, i| { |
| 269 | if ((regs >> @intCast(u5, i)) & 1 == 0) continue; | 270 | if ((regs >> @intCast(u5, i)) & 1 == 0) continue; |
| 270 | if (tag == .push) { | 271 | if (tag == .push) { |
| 271 | try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ | 272 | try lowerToMrEnc(.mov, RegisterOrMemory.mem(.qword_ptr, .{ |
src/arch/x86_64/PrintMir.zig+2-1| ... | @@ -5,6 +5,7 @@ const Print = @This(); | ... | @@ -5,6 +5,7 @@ const Print = @This(); |
| 5 | const std = @import("std"); | 5 | const std = @import("std"); |
| 6 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 7 | const bits = @import("bits.zig"); | 7 | const bits = @import("bits.zig"); |
| 8 | const abi = @import("abi.zig"); | ||
| 8 | const leb128 = std.leb; | 9 | const leb128 = std.leb; |
| 9 | const link = @import("../../link.zig"); | 10 | const link = @import("../../link.zig"); |
| 10 | const log = std.log.scoped(.codegen); | 11 | const log = std.log.scoped(.codegen); |
| ... | @@ -188,7 +189,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(print: *const Print, tag: Mir.Inst.Tag, | ... | @@ -188,7 +189,7 @@ fn mirPushPopRegsFromCalleePreservedRegs(print: *const Print, tag: Mir.Inst.Tag, |
| 188 | var disp: u32 = data.disp + 8; | 189 | var disp: u32 = data.disp + 8; |
| 189 | if (regs == 0) return w.writeAll("no regs from callee_preserved_regs\n"); | 190 | if (regs == 0) return w.writeAll("no regs from callee_preserved_regs\n"); |
| 190 | var printed_first_reg = false; | 191 | var printed_first_reg = false; |
| 191 | for (bits.callee_preserved_regs) |reg, i| { | 192 | for (abi.callee_preserved_regs) |reg, i| { |
| 192 | if ((regs >> @intCast(u5, i)) & 1 == 0) continue; | 193 | if ((regs >> @intCast(u5, i)) & 1 == 0) continue; |
| 193 | if (printed_first_reg) try w.writeAll(" "); | 194 | if (printed_first_reg) try w.writeAll(" "); |
| 194 | printed_first_reg = true; | 195 | printed_first_reg = true; |
src/arch/x86_64/abi.zig+7| ... | @@ -2,6 +2,7 @@ const std = @import("std"); | ... | @@ -2,6 +2,7 @@ const std = @import("std"); |
| 2 | const Type = @import("../../type.zig").Type; | 2 | const Type = @import("../../type.zig").Type; |
| 3 | const Target = std.Target; | 3 | const Target = std.Target; |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const Register = @import("bits.zig").Register; | ||
| 5 | 6 | ||
| 6 | pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none }; | 7 | pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none }; |
| 7 | 8 | ||
| ... | @@ -370,3 +371,9 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { | ... | @@ -370,3 +371,9 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { |
| 370 | else => unreachable, | 371 | else => unreachable, |
| 371 | } | 372 | } |
| 372 | } | 373 | } |
| 374 | |||
| 375 | /// These registers need to be preserved (saved on the stack) and restored by the callee before getting clobbered | ||
| 376 | /// and when the callee returns. | ||
| 377 | pub const callee_preserved_regs = [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | ||
| 378 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | ||
| 379 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; |
src/arch/x86_64/bits.zig+2-23| ... | @@ -30,14 +30,14 @@ pub const Register = enum(u7) { | ... | @@ -30,14 +30,14 @@ pub const Register = enum(u7) { |
| 30 | 30 | ||
| 31 | // 16 through 31, 32-bit registers. 24-31 are extended. | 31 | // 16 through 31, 32-bit registers. 24-31 are extended. |
| 32 | // id is int value - 16. | 32 | // id is int value - 16. |
| 33 | eax, ecx, edx, ebx, esp, ebp, esi, edi, | 33 | eax, ecx, edx, ebx, esp, ebp, esi, edi, |
| 34 | r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, | 34 | r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, |
| 35 | 35 | ||
| 36 | // 32-47, 16-bit registers. 40-47 are extended. | 36 | // 32-47, 16-bit registers. 40-47 are extended. |
| 37 | // id is int value - 32. | 37 | // id is int value - 32. |
| 38 | ax, cx, dx, bx, sp, bp, si, di, | 38 | ax, cx, dx, bx, sp, bp, si, di, |
| 39 | r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, | 39 | r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, |
| 40 | 40 | ||
| 41 | // 48-63, 8-bit registers. 56-63 are extended. | 41 | // 48-63, 8-bit registers. 56-63 are extended. |
| 42 | // id is int value - 48. | 42 | // id is int value - 48. |
| 43 | al, cl, dl, bl, ah, ch, dh, bh, | 43 | al, cl, dl, bl, ah, ch, dh, bh, |
| ... | @@ -81,20 +81,6 @@ pub const Register = enum(u7) { | ... | @@ -81,20 +81,6 @@ pub const Register = enum(u7) { |
| 81 | return @truncate(u3, @enumToInt(self)); | 81 | return @truncate(u3, @enumToInt(self)); |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | /// Returns the index into `callee_preserved_regs`. | ||
| 85 | pub fn allocIndex(self: Register) ?u4 { | ||
| 86 | return switch (self) { | ||
| 87 | .rcx, .ecx, .cx, .cl => 0, | ||
| 88 | .rsi, .esi, .si => 1, | ||
| 89 | .rdi, .edi, .di => 2, | ||
| 90 | .r8, .r8d, .r8w, .r8b => 3, | ||
| 91 | .r9, .r9d, .r9w, .r9b => 4, | ||
| 92 | .r10, .r10d, .r10w, .r10b => 5, | ||
| 93 | .r11, .r11d, .r11w, .r11b => 6, | ||
| 94 | else => null, | ||
| 95 | }; | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Convert from any register to its 64 bit alias. | 84 | /// Convert from any register to its 64 bit alias. |
| 99 | pub fn to64(self: Register) Register { | 85 | pub fn to64(self: Register) Register { |
| 100 | return @intToEnum(Register, self.id()); | 86 | return @intToEnum(Register, self.id()); |
| ... | @@ -142,13 +128,6 @@ pub const Register = enum(u7) { | ... | @@ -142,13 +128,6 @@ pub const Register = enum(u7) { |
| 142 | 128 | ||
| 143 | // zig fmt: on | 129 | // zig fmt: on |
| 144 | 130 | ||
| 145 | /// TODO this set is actually a set of caller-saved registers. | ||
| 146 | /// These registers need to be preserved (saved on the stack) and restored by the callee before getting clobbered | ||
| 147 | /// and when the callee returns. | ||
| 148 | pub const callee_preserved_regs = [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; | ||
| 149 | pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; | ||
| 150 | pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; | ||
| 151 | |||
| 152 | /// Encoding helper functions for x86_64 instructions | 131 | /// Encoding helper functions for x86_64 instructions |
| 153 | /// | 132 | /// |
| 154 | /// Many of these helpers do very little, but they can help make things | 133 | /// Many of these helpers do very little, but they can help make things |
src/register_manager.zig+35-28| ... | @@ -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 { |
| ... | @@ -61,7 +61,7 @@ pub fn RegisterManager( | ... | @@ -61,7 +61,7 @@ pub fn RegisterManager( |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | fn getRegisterMask(reg: Register) ?FreeRegInt { | 63 | fn getRegisterMask(reg: Register) ?FreeRegInt { |
| 64 | const index = reg.allocIndex() orelse return null; | 64 | const index = indexOfRegIntoTracked(reg) orelse return null; |
| 65 | const shift = @intCast(ShiftInt, index); | 65 | const shift = @intCast(ShiftInt, index); |
| 66 | const mask = @as(FreeRegInt, 1) << shift; | 66 | const mask = @as(FreeRegInt, 1) << shift; |
| 67 | return mask; | 67 | return mask; |
| ... | @@ -82,6 +82,17 @@ pub fn RegisterManager( | ... | @@ -82,6 +82,17 @@ pub fn RegisterManager( |
| 82 | self.free_registers |= mask; | 82 | self.free_registers |= mask; |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) { | ||
| 86 | inline for (tracked_registers) |cpreg, i| { | ||
| 87 | if (reg.id() == cpreg.id()) return i; | ||
| 88 | } | ||
| 89 | return null; | ||
| 90 | } | ||
| 91 | |||
| 92 | pub fn indexOfRegIntoTracked(reg: Register) ?ShiftInt { | ||
| 93 | return indexOfReg(tracked_registers, reg); | ||
| 94 | } | ||
| 95 | |||
| 85 | /// Returns true when this register is not tracked | 96 | /// Returns true when this register is not tracked |
| 86 | pub fn isRegFree(self: Self, reg: Register) bool { | 97 | pub fn isRegFree(self: Self, reg: Register) bool { |
| 87 | const mask = getRegisterMask(reg) orelse return true; | 98 | const mask = getRegisterMask(reg) orelse return true; |
| ... | @@ -135,14 +146,14 @@ pub fn RegisterManager( | ... | @@ -135,14 +146,14 @@ pub fn RegisterManager( |
| 135 | comptime count: comptime_int, | 146 | comptime count: comptime_int, |
| 136 | insts: [count]?Air.Inst.Index, | 147 | insts: [count]?Air.Inst.Index, |
| 137 | ) ?[count]Register { | 148 | ) ?[count]Register { |
| 138 | comptime assert(count > 0 and count <= callee_preserved_regs.len); | 149 | comptime assert(count > 0 and count <= tracked_registers.len); |
| 139 | 150 | ||
| 140 | const free_registers = @popCount(FreeRegInt, self.free_registers); | 151 | const free_registers = @popCount(FreeRegInt, self.free_registers); |
| 141 | if (free_registers < count) return null; | 152 | if (free_registers < count) return null; |
| 142 | 153 | ||
| 143 | var regs: [count]Register = undefined; | 154 | var regs: [count]Register = undefined; |
| 144 | var i: usize = 0; | 155 | var i: usize = 0; |
| 145 | for (callee_preserved_regs) |reg| { | 156 | for (tracked_registers) |reg| { |
| 146 | if (i >= count) break; | 157 | if (i >= count) break; |
| 147 | if (self.isRegFrozen(reg)) continue; | 158 | if (self.isRegFrozen(reg)) continue; |
| 148 | if (self.isRegFree(reg)) { | 159 | if (self.isRegFree(reg)) { |
| ... | @@ -157,7 +168,7 @@ pub fn RegisterManager( | ... | @@ -157,7 +168,7 @@ pub fn RegisterManager( |
| 157 | 168 | ||
| 158 | if (insts[j]) |inst| { | 169 | if (insts[j]) |inst| { |
| 159 | // Track the register | 170 | // Track the register |
| 160 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null | 171 | const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null |
| 161 | self.registers[index] = inst; | 172 | self.registers[index] = inst; |
| 162 | self.markRegUsed(reg); | 173 | self.markRegUsed(reg); |
| 163 | } | 174 | } |
| ... | @@ -181,8 +192,8 @@ pub fn RegisterManager( | ... | @@ -181,8 +192,8 @@ pub fn RegisterManager( |
| 181 | comptime count: comptime_int, | 192 | comptime count: comptime_int, |
| 182 | insts: [count]?Air.Inst.Index, | 193 | insts: [count]?Air.Inst.Index, |
| 183 | ) AllocateRegistersError![count]Register { | 194 | ) AllocateRegistersError![count]Register { |
| 184 | comptime assert(count > 0 and count <= callee_preserved_regs.len); | 195 | comptime assert(count > 0 and count <= tracked_registers.len); |
| 185 | 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; |
| 186 | 197 | ||
| 187 | const result = self.tryAllocRegs(count, insts) orelse blk: { | 198 | const result = self.tryAllocRegs(count, insts) orelse blk: { |
| 188 | // We'll take over the first count registers. Spill | 199 | // We'll take over the first count registers. Spill |
| ... | @@ -190,13 +201,13 @@ pub fn RegisterManager( | ... | @@ -190,13 +201,13 @@ pub fn RegisterManager( |
| 190 | // stack allocations. | 201 | // stack allocations. |
| 191 | var regs: [count]Register = undefined; | 202 | var regs: [count]Register = undefined; |
| 192 | var i: usize = 0; | 203 | var i: usize = 0; |
| 193 | for (callee_preserved_regs) |reg| { | 204 | for (tracked_registers) |reg| { |
| 194 | if (i >= count) break; | 205 | if (i >= count) break; |
| 195 | if (self.isRegFrozen(reg)) continue; | 206 | if (self.isRegFrozen(reg)) continue; |
| 196 | 207 | ||
| 197 | regs[i] = reg; | 208 | regs[i] = reg; |
| 198 | self.markRegAllocated(reg); | 209 | self.markRegAllocated(reg); |
| 199 | const index = reg.allocIndex().?; // allocIndex() on a callee-preserved reg should never return null | 210 | const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null |
| 200 | if (insts[i]) |inst| { | 211 | if (insts[i]) |inst| { |
| 201 | // Track the register | 212 | // Track the register |
| 202 | if (self.isRegFree(reg)) { | 213 | if (self.isRegFree(reg)) { |
| ... | @@ -235,7 +246,8 @@ pub fn RegisterManager( | ... | @@ -235,7 +246,8 @@ pub fn RegisterManager( |
| 235 | /// corresponding instruction is passed, will also track this | 246 | /// corresponding instruction is passed, will also track this |
| 236 | /// register. | 247 | /// register. |
| 237 | 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 { |
| 238 | const index = reg.allocIndex() orelse return; | 249 | const index = indexOfRegIntoTracked(reg) orelse return; |
| 250 | log.debug("getReg {} for inst {}", .{ reg, inst }); | ||
| 239 | self.markRegAllocated(reg); | 251 | self.markRegAllocated(reg); |
| 240 | 252 | ||
| 241 | if (inst) |tracked_inst| | 253 | if (inst) |tracked_inst| |
| ... | @@ -263,7 +275,8 @@ pub fn RegisterManager( | ... | @@ -263,7 +275,8 @@ pub fn RegisterManager( |
| 263 | /// instruction. Asserts that the register is free and no | 275 | /// instruction. Asserts that the register is free and no |
| 264 | /// spilling is necessary. | 276 | /// spilling is necessary. |
| 265 | 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 { |
| 266 | const index = reg.allocIndex() orelse return; | 278 | const index = indexOfRegIntoTracked(reg) orelse return; |
| 279 | log.debug("getRegAssumeFree {} for inst {}", .{ reg, inst }); | ||
| 267 | self.markRegAllocated(reg); | 280 | self.markRegAllocated(reg); |
| 268 | 281 | ||
| 269 | assert(self.isRegFree(reg)); | 282 | assert(self.isRegFree(reg)); |
| ... | @@ -273,7 +286,7 @@ pub fn RegisterManager( | ... | @@ -273,7 +286,7 @@ pub fn RegisterManager( |
| 273 | 286 | ||
| 274 | /// Marks the specified register as free | 287 | /// Marks the specified register as free |
| 275 | pub fn freeReg(self: *Self, reg: Register) void { | 288 | pub fn freeReg(self: *Self, reg: Register) void { |
| 276 | const index = reg.allocIndex() orelse return; | 289 | const index = indexOfRegIntoTracked(reg) orelse return; |
| 277 | log.debug("freeing register {}", .{reg}); | 290 | log.debug("freeing register {}", .{reg}); |
| 278 | 291 | ||
| 279 | self.registers[index] = undefined; | 292 | self.registers[index] = undefined; |
| ... | @@ -288,14 +301,11 @@ const MockRegister1 = enum(u2) { | ... | @@ -288,14 +301,11 @@ const MockRegister1 = enum(u2) { |
| 288 | r2, | 301 | r2, |
| 289 | r3, | 302 | r3, |
| 290 | 303 | ||
| 291 | pub fn allocIndex(self: MockRegister1) ?u2 { | 304 | pub fn id(reg: MockRegister1) u2 { |
| 292 | inline for (callee_preserved_regs) |cpreg, i| { | 305 | return @enumToInt(reg); |
| 293 | if (self == cpreg) return i; | ||
| 294 | } | ||
| 295 | return null; | ||
| 296 | } | 306 | } |
| 297 | 307 | ||
| 298 | const callee_preserved_regs = [_]MockRegister1{ .r2, .r3 }; | 308 | const allocatable_registers = [_]MockRegister1{ .r2, .r3 }; |
| 299 | }; | 309 | }; |
| 300 | 310 | ||
| 301 | const MockRegister2 = enum(u2) { | 311 | const MockRegister2 = enum(u2) { |
| ... | @@ -304,20 +314,17 @@ const MockRegister2 = enum(u2) { | ... | @@ -304,20 +314,17 @@ const MockRegister2 = enum(u2) { |
| 304 | r2, | 314 | r2, |
| 305 | r3, | 315 | r3, |
| 306 | 316 | ||
| 307 | pub fn allocIndex(self: MockRegister2) ?u2 { | 317 | pub fn id(reg: MockRegister2) u2 { |
| 308 | inline for (callee_preserved_regs) |cpreg, i| { | 318 | return @enumToInt(reg); |
| 309 | if (self == cpreg) return i; | ||
| 310 | } | ||
| 311 | return null; | ||
| 312 | } | 319 | } |
| 313 | 320 | ||
| 314 | const callee_preserved_regs = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; | 321 | const allocatable_registers = [_]MockRegister2{ .r0, .r1, .r2, .r3 }; |
| 315 | }; | 322 | }; |
| 316 | 323 | ||
| 317 | fn MockFunction(comptime Register: type) type { | 324 | fn MockFunction(comptime Register: type) type { |
| 318 | return struct { | 325 | return struct { |
| 319 | allocator: Allocator, | 326 | allocator: Allocator, |
| 320 | register_manager: RegisterManager(Self, Register, &Register.callee_preserved_regs) = .{}, | 327 | register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{}, |
| 321 | spilled: std.ArrayListUnmanaged(Register) = .{}, | 328 | spilled: std.ArrayListUnmanaged(Register) = .{}, |
| 322 | 329 | ||
| 323 | const Self = @This(); | 330 | const Self = @This(); |