const Type = @import("../Type.zig"); // zig fmt: off /// Definitions of all of the x64 registers. The order is semantically meaningful. /// The registers are defined such that IDs go in descending order of 64-bit, /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen /// registers. This results in some useful properties: /// /// Any 64-bit register can be turned into its 32-bit form by adding 16, and /// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it /// works for all except for sp, bp, si, and di, which do *not* have an 8-bit /// form. /// /// If (register & 8) is set, the register is extended. /// /// The ID can be easily determined by figuring out what range the register is /// in, and then subtracting the base. pub const Register = enum(u8) { // 0 through 15, 64-bit registers. 8-15 are extended. // id is just the int value. rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15, // 16 through 31, 32-bit registers. 24-31 are extended. // id is int value - 16. eax, ecx, edx, ebx, esp, ebp, esi, edi, r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, // 32-47, 16-bit registers. 40-47 are extended. // id is int value - 32. ax, cx, dx, bx, sp, bp, si, di, r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, // 48-63, 8-bit registers. 56-63 are extended. // id is int value - 48. al, cl, dl, bl, ah, ch, dh, bh, r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, /// Returns the bit-width of the register. pub fn size(self: Register) u7 { return switch (@enumToInt(self)) { 0...15 => 64, 16...31 => 32, 32...47 => 16, 48...64 => 8, else => unreachable, }; } /// Returns whether the register is *extended*. Extended registers are the /// new registers added with amd64, r8 through r15. This also includes any /// other variant of access to those registers, such as r8b, r15d, and so /// on. This is needed because access to these registers requires special /// handling via the REX prefix, via the B or R bits, depending on context. pub fn isExtended(self: Register) bool { return @enumToInt(self) & 0x08 != 0; } /// This returns the 4-bit register ID, which is used in practically every /// opcode. Note that bit 3 (the highest bit) is *never* used directly in /// an instruction (@see isExtended), and requires special handling. The /// lower three bits are often embedded directly in instructions (such as /// the B8 variant of moves), or used in R/M bytes. pub fn id(self: Register) u4 { return @truncate(u4, @enumToInt(self)); } /// Returns the index into `callee_preserved_regs`. pub fn allocIndex(self: Register) ?u4 { return switch (self) { .rax, .eax, .ax, .al => 0, .rcx, .ecx, .cx, .cl => 1, .rdx, .edx, .dx, .dl => 2, .rsi, .esi, .si => 3, .rdi, .edi, .di => 4, .r8, .r8d, .r8w, .r8b => 5, .r9, .r9d, .r9w, .r9b => 6, .r10, .r10d, .r10w, .r10b => 7, .r11, .r11d, .r11w, .r11b => 8, else => null, }; } /// Convert from any register to its 64 bit alias. pub fn to64(self: Register) Register { return @intToEnum(Register, self.id()); } /// Convert from any register to its 32 bit alias. pub fn to32(self: Register) Register { return @intToEnum(Register, @as(u8, self.id()) + 16); } /// Convert from any register to its 16 bit alias. pub fn to16(self: Register) Register { return @intToEnum(Register, @as(u8, self.id()) + 32); } /// Convert from any register to its 8 bit alias. pub fn to8(self: Register) Register { return @intToEnum(Register, @as(u8, self.id()) + 48); } }; // zig fmt: on /// These registers belong to the called function. pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };