diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 149 | ||||
| -rw-r--r-- | src/arch/aarch64/Emit.zig | 8 | ||||
| -rw-r--r-- | src/arch/aarch64/bits.zig | 389 |
3 files changed, 336 insertions, 210 deletions
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 75dfcdbae6..a8248eff85 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -414,7 +414,7 @@ fn gen(self: *Self) !void { // to the stack. const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes = @divExact(ptr_bits, 8); - const ret_ptr_reg = registerAlias(.x0, ptr_bytes); + const ret_ptr_reg = self.registerAlias(.x0, Type.usize); const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, ptr_bytes) + ptr_bytes; self.next_stack_offset = stack_offset; @@ -927,7 +927,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { // Make sure the type can fit in a register before we try to allocate one. if (abi_size <= 8) { if (self.register_manager.tryAllocReg(inst, gp)) |reg| { - return MCValue{ .register = registerAlias(reg, abi_size) }; + return MCValue{ .register = self.registerAlias(reg, elem_ty) }; } } } @@ -982,7 +982,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { /// This can have a side effect of spilling instructions to the stack to free up a register. fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { const raw_reg = try self.register_manager.allocReg(null, gp); - const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); + const reg = self.registerAlias(raw_reg, ty); try self.genSetReg(ty, reg, mcv); return reg; } @@ -993,7 +993,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { const raw_reg = try self.register_manager.allocReg(reg_owner, gp); const ty = self.air.typeOfIndex(reg_owner); - const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); + const reg = self.registerAlias(raw_reg, ty); try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); return MCValue{ .register = reg }; } @@ -1031,7 +1031,6 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { const operand_info = operand_ty.intInfo(self.target.*); const dest_ty = self.air.typeOfIndex(inst); - const dest_abi_size = dest_ty.abiSize(self.target.*); const dest_info = dest_ty.intInfo(self.target.*); const result: MCValue = result: { @@ -1042,7 +1041,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); const truncated: MCValue = switch (operand_mcv) { - .register => |r| MCValue{ .register = registerAlias(r, dest_abi_size) }, + .register => |r| MCValue{ .register = self.registerAlias(r, dest_ty) }, else => operand_mcv, }; @@ -1117,7 +1116,7 @@ fn trunc( else => operand_reg: { if (info_a.bits <= 64) { const raw_reg = try self.copyToTmpRegister(operand_ty, operand); - break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); + break :operand_reg self.registerAlias(raw_reg, operand_ty); } else { return self.fail("TODO load least significant word into register", .{}); } @@ -1130,14 +1129,14 @@ fn trunc( const ty_op = self.air.instructions.items(.data)[inst].ty_op; if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { - break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); + break :blk self.registerAlias(operand_reg, dest_ty); } else { const raw_reg = try self.register_manager.allocReg(inst, gp); - break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); + break :blk self.registerAlias(raw_reg, dest_ty); } } else blk: { const raw_reg = try self.register_manager.allocReg(null, gp); - break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); + break :blk self.registerAlias(raw_reg, dest_ty); }; try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); @@ -1194,7 +1193,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { } const raw_reg = try self.register_manager.allocReg(null, gp); - break :blk raw_reg.to32(); + break :blk self.registerAlias(raw_reg, operand_ty); }; _ = try self.addInst(.{ @@ -1227,7 +1226,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { } const raw_reg = try self.register_manager.allocReg(null, gp); - break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); + break :blk self.registerAlias(raw_reg, operand_ty); }; _ = try self.addInst(.{ @@ -1307,8 +1306,8 @@ fn binOpRegister( const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; - if (lhs_is_register) assert(lhs.register == registerAlias(lhs.register, lhs_ty.abiSize(self.target.*))); - if (rhs_is_register) assert(rhs.register == registerAlias(rhs.register, rhs_ty.abiSize(self.target.*))); + if (lhs_is_register) assert(lhs.register == self.registerAlias(lhs.register, lhs_ty)); + if (rhs_is_register) assert(rhs.register == self.registerAlias(rhs.register, rhs_ty)); const lhs_lock: ?RegisterLock = if (lhs_is_register) self.register_manager.lockReg(lhs.register) @@ -1330,7 +1329,7 @@ fn binOpRegister( } else null; const raw_reg = try self.register_manager.allocReg(track_inst, gp); - const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + const reg = self.registerAlias(raw_reg, lhs_ty); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1344,7 +1343,7 @@ fn binOpRegister( // order to guarantee that registers will have equal sizes, we // use the register alias of rhs corresponding to the size of // lhs. - registerAlias(rhs.register, lhs_ty.abiSize(self.target.*)) + self.registerAlias(rhs.register, lhs_ty) else blk: { const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { break :inst Air.refToIndex(md.rhs).?; @@ -1354,7 +1353,7 @@ fn binOpRegister( // Here, we deliberately use lhs as lhs and rhs may differ in // the case of shifts. See comment above. - const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + const reg = self.registerAlias(raw_reg, lhs_ty); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1372,11 +1371,11 @@ fn binOpRegister( break :blk rhs_reg; } else { const raw_reg = try self.register_manager.allocReg(md.inst, gp); - break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + break :blk self.registerAlias(raw_reg, lhs_ty); } } else blk: { const raw_reg = try self.register_manager.allocReg(null, gp); - break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + break :blk self.registerAlias(raw_reg, lhs_ty); }, }; @@ -1415,7 +1414,7 @@ fn binOpRegister( .smull, .umull, => .{ .rrr = .{ - .rd = dest_reg.to64(), + .rd = dest_reg.toX(), .rn = lhs_reg, .rm = rhs_reg, } }, @@ -1463,7 +1462,7 @@ fn binOpImmediate( ) !MCValue { const lhs_is_register = lhs == .register; - if (lhs_is_register) assert(lhs.register == registerAlias(lhs.register, lhs_ty.abiSize(self.target.*))); + if (lhs_is_register) assert(lhs.register == self.registerAlias(lhs.register, lhs_ty)); const lhs_lock: ?RegisterLock = if (lhs_is_register) self.register_manager.lockReg(lhs.register) @@ -1481,7 +1480,7 @@ fn binOpImmediate( } else null; const raw_reg = try self.register_manager.allocReg(track_inst, gp); - const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + const reg = self.registerAlias(raw_reg, lhs_ty); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1502,11 +1501,11 @@ fn binOpImmediate( break :blk lhs_reg; } else { const raw_reg = try self.register_manager.allocReg(md.inst, gp); - break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + break :blk self.registerAlias(raw_reg, lhs_ty); } } else blk: { const raw_reg = try self.register_manager.allocReg(null, gp); - break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + break :blk self.registerAlias(raw_reg, lhs_ty); }, }; @@ -1719,7 +1718,7 @@ fn binOp( } else null; const raw_reg = try self.register_manager.allocReg(track_inst, gp); - const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + const reg = self.registerAlias(raw_reg, lhs_ty); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1734,7 +1733,7 @@ fn binOp( } else null; const raw_reg = try self.register_manager.allocReg(track_inst, gp); - const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); + const reg = self.registerAlias(raw_reg, rhs_ty); if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); @@ -1745,10 +1744,9 @@ fn binOp( const dest_regs: [2]Register = blk: { const raw_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); - const abi_size = lhs_ty.abiSize(self.target.*); break :blk .{ - registerAlias(raw_regs[0], abi_size), - registerAlias(raw_regs[1], abi_size), + self.registerAlias(raw_regs[0], lhs_ty), + self.registerAlias(raw_regs[1], lhs_ty), }; }; const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs); @@ -2067,7 +2065,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(dest_reg_lock); const raw_truncated_reg = try self.register_manager.allocReg(null, gp); - const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); + const truncated_reg = self.registerAlias(raw_truncated_reg, lhs_ty); const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); defer self.register_manager.unlockReg(truncated_reg_lock); @@ -2186,8 +2184,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { defer self.register_manager.unlockReg(truncated_reg_lock); try self.truncRegister( - dest_reg.to32(), - truncated_reg.to32(), + dest_reg.toW(), + truncated_reg.toW(), int_info.signedness, int_info.bits, ); @@ -2197,8 +2195,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.addInst(.{ .tag = .cmp_extended_register, .data = .{ .rr_extend_shift = .{ - .rn = dest_reg.to64(), - .rm = truncated_reg.to32(), + .rn = dest_reg.toX(), + .rm = truncated_reg.toW(), .ext_type = .sxtw, .imm3 = 0, } }, @@ -2208,8 +2206,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.addInst(.{ .tag = .cmp_extended_register, .data = .{ .rr_extend_shift = .{ - .rn = dest_reg.to64(), - .rm = truncated_reg.to32(), + .rn = dest_reg.toX(), + .rm = truncated_reg.toW(), .ext_type = .uxtw, .imm3 = 0, } }, @@ -2245,7 +2243,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const lhs_reg = if (lhs_is_register) lhs.register else blk: { const raw_reg = try self.register_manager.allocReg(null, gp); - const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + const reg = self.registerAlias(raw_reg, lhs_ty); break :blk reg; }; const new_lhs_lock = self.register_manager.lockReg(lhs_reg); @@ -2253,7 +2251,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const rhs_reg = if (rhs_is_register) rhs.register else blk: { const raw_reg = try self.register_manager.allocReg(null, gp); - const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); + const reg = self.registerAlias(raw_reg, rhs_ty); break :blk reg; }; const new_rhs_lock = self.register_manager.lockReg(rhs_reg); @@ -2264,7 +2262,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const dest_reg = blk: { const raw_reg = try self.register_manager.allocReg(null, gp); - const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); + const reg = self.registerAlias(raw_reg, lhs_ty); break :blk reg; }; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); @@ -2853,7 +2851,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ un_op, .none, .none }); } -fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { +fn reuseOperand( + self: *Self, + inst: Air.Inst.Index, + operand: Air.Inst.Ref, + op_index: Liveness.OperandInt, + mcv: MCValue, +) bool { if (!self.liveness.operandDies(inst, op_index)) return false; @@ -2912,7 +2916,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .stack_offset => |off| { if (elem_size <= 8) { const raw_tmp_reg = try self.register_manager.allocReg(null, gp); - const tmp_reg = registerAlias(raw_tmp_reg, elem_size); + const tmp_reg = self.registerAlias(raw_tmp_reg, elem_ty); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -3050,7 +3054,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { // The MCValue that holds the pointer can be re-used as the value. break :blk switch (ptr) { - .register => |r| MCValue{ .register = registerAlias(r, elem_size) }, + .register => |reg| MCValue{ .register = self.registerAlias(reg, elem_ty) }, else => ptr, }; } else { @@ -3136,7 +3140,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type else => { if (abi_size <= 8) { const raw_tmp_reg = try self.register_manager.allocReg(null, gp); - const tmp_reg = registerAlias(raw_tmp_reg, abi_size); + const tmp_reg = self.registerAlias(raw_tmp_reg, value_ty); const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_reg_lock); @@ -3295,7 +3299,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { } else { // Copy to new register const raw_dest_reg = try self.register_manager.allocReg(null, gp); - const dest_reg = registerAlias(raw_dest_reg, struct_field_ty.abiSize(self.target.*)); + const dest_reg = self.registerAlias(raw_dest_reg, struct_field_ty); try self.genSetReg(struct_field_ty, dest_reg, field); break :result MCValue{ .register = dest_reg }; @@ -3410,9 +3414,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); const stack_offset = try self.allocMem(inst, ret_abi_size, ret_abi_align); - const ptr_bits = self.target.cpu.arch.ptrBitWidth(); - const ptr_bytes = @divExact(ptr_bits, 8); - const ret_ptr_reg = registerAlias(.x0, ptr_bytes); + const ret_ptr_reg = self.registerAlias(.x0, Type.usize); var ptr_ty_payload: Type.Payload.ElemType = .{ .base = .{ .tag = .single_mut_pointer }, @@ -4376,7 +4378,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro 4, 8 => .str_stack, else => unreachable, // unexpected abi size }; - const rt = registerAlias(reg, abi_size); + const rt = self.registerAlias(reg, ty); _ = try self.addInst(.{ .tag = tag, @@ -4399,10 +4401,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const overflow_bit_ty = ty.structFieldType(1); const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); const raw_cond_reg = try self.register_manager.allocReg(null, gp); - const cond_reg = registerAlias( - raw_cond_reg, - @intCast(u32, overflow_bit_ty.abiSize(self.target.*)), - ); + const cond_reg = self.registerAlias(raw_cond_reg, overflow_bit_ty); _ = try self.addInst(.{ .tag = .cset, @@ -4599,8 +4598,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .memory => |addr| { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(ty, reg.to64(), .{ .immediate = addr }); - try self.genLdrRegister(reg, reg.to64(), ty); + try self.genSetReg(ty, reg.toX(), .{ .immediate = addr }); + try self.genLdrRegister(reg, reg.toX(), ty); }, .stack_offset => |off| { const abi_size = ty.abiSize(self.target.*); @@ -4679,7 +4678,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I 4, 8 => .str_immediate, else => unreachable, // unexpected abi size }; - const rt = registerAlias(reg, abi_size); + const rt = self.registerAlias(reg, ty); const offset = switch (abi_size) { 1 => blk: { if (math.cast(u12, stack_offset)) |imm| { @@ -5300,7 +5299,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { assert(ret_ty.isError()); result.return_value = .{ .immediate = 0 }; } else if (ret_ty_size <= 8) { - result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; + result.return_value = .{ .register = self.registerAlias(c_abi_int_return_regs[0], ret_ty) }; } else { return self.fail("TODO support more return types for ARM backend", .{}); } @@ -5322,7 +5321,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { if (param_size <= 8) { - result.args[i] = .{ .register = registerAlias(c_abi_int_param_regs[ncrn], param_size) }; + result.args[i] = .{ .register = self.registerAlias(c_abi_int_param_regs[ncrn], ty) }; ncrn += 1; } else { return self.fail("TODO MCValues with multiple registers", .{}); @@ -5358,7 +5357,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { assert(ret_ty.isError()); result.return_value = .{ .immediate = 0 }; } else if (ret_ty_size <= 8) { - result.return_value = .{ .register = registerAlias(.x0, ret_ty_size) }; + result.return_value = .{ .register = self.registerAlias(.x0, ret_ty) }; } else { // The result is returned by reference, not by // value. This means that x0 (or w0 when pointer @@ -5424,14 +5423,30 @@ fn parseRegName(name: []const u8) ?Register { return std.meta.stringToEnum(Register, name); } -fn registerAlias(reg: Register, size_bytes: u64) Register { - if (size_bytes == 0) { - unreachable; // should be comptime-known - } else if (size_bytes <= 4) { - return reg.to32(); - } else if (size_bytes <= 8) { - return reg.to64(); - } else { - unreachable; // TODO handle floating-point registers +fn registerAlias(self: *Self, reg: Register, ty: Type) Register { + const abi_size = ty.abiSize(self.target.*); + + switch (reg.class()) { + .general_purpose => { + if (abi_size == 0) { + unreachable; // should be comptime-known + } else if (abi_size <= 4) { + return reg.toW(); + } else if (abi_size <= 8) { + return reg.toX(); + } else unreachable; + }, + .stack_pointer => unreachable, // we can't store/load the sp + .floating_point => { + return switch (ty.floatBits(self.target.*)) { + 16 => reg.toH(), + 32 => reg.toS(), + 64 => reg.toD(), + 128 => reg.toQ(), + + 80 => unreachable, // f80 registers don't exist + else => unreachable, + }; + }, } } diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index abcbf15a05..54e40c776f 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -842,14 +842,14 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { // PC-relative displacement to the entry in memory. // adrp const offset = @intCast(u32, emit.code.items.len); - try emit.writeInstruction(Instruction.adrp(reg.to64(), 0)); + try emit.writeInstruction(Instruction.adrp(reg.toX(), 0)); switch (tag) { .load_memory_got => { // ldr reg, reg, offset try emit.writeInstruction(Instruction.ldr( reg, - reg.to64(), + reg.toX(), Instruction.LoadStoreOffset.imm(0), )); }, @@ -863,11 +863,11 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { // Note that this can potentially be optimised out by the codegen/linker if the // target address is appropriately aligned. // add reg, reg, offset - try emit.writeInstruction(Instruction.add(reg.to64(), reg.to64(), 0, false)); + try emit.writeInstruction(Instruction.add(reg.toX(), reg.toX(), 0, false)); // ldr reg, reg, offset try emit.writeInstruction(Instruction.ldr( reg, - reg.to64(), + reg.toX(), Instruction.LoadStoreOffset.imm(0), )); }, diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index ad45661b70..aa13298afe 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -4,17 +4,22 @@ const DW = std.dwarf; const assert = std.debug.assert; const testing = std.testing; -// zig fmt: off +pub const RegisterClass = enum { + general_purpose, + stack_pointer, + floating_point, +}; /// General purpose registers in the AArch64 instruction set -pub const Register = enum(u7) { - // 64-bit registers +pub const Register = enum(u8) { + // zig fmt: off + // 64-bit general-purpose registers x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30, xzr, - // 32-bit registers + // 32-bit general-purpose registers w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, @@ -23,192 +28,298 @@ pub const Register = enum(u7) { // Stack pointer sp, wsp, - pub fn id(self: Register) u6 { - return switch (@enumToInt(self)) { - 0...63 => return @as(u6, @truncate(u5, @enumToInt(self))), - 64...65 => 32, - else => unreachable, - }; - } - - pub fn enc(self: Register) u5 { - return switch (@enumToInt(self)) { - 0...63 => return @truncate(u5, @enumToInt(self)), - 64...65 => 31, - else => unreachable, - }; - } - - /// Returns the bit-width of the register. - pub fn size(self: Register) u7 { - return switch (@enumToInt(self)) { - 0...31 => 64, - 32...63 => 32, - 64 => 64, - 65 => 32, - else => unreachable, - }; - } - - /// Convert from any register to its 64 bit alias. - pub fn to64(self: Register) Register { - return switch (@enumToInt(self)) { - 0...31 => self, - 32...63 => @intToEnum(Register, @enumToInt(self) - 32), - 64 => .sp, - 65 => .sp, - else => unreachable, - }; - } - - /// Convert from any register to its 32 bit alias. - pub fn to32(self: Register) Register { - return switch (@enumToInt(self)) { - 0...31 => @intToEnum(Register, @enumToInt(self) + 32), - 32...63 => self, - 64 => .wsp, - 65 => .wsp, - else => unreachable, - }; - } - - pub fn dwarfLocOp(self: Register) u8 { - return @as(u8, self.enc()) + DW.OP.reg0; - } -}; - -// zig fmt: on - -test "Register.enc" { - try testing.expectEqual(@as(u5, 0), Register.x0.enc()); - try testing.expectEqual(@as(u5, 0), Register.w0.enc()); - - try testing.expectEqual(@as(u5, 31), Register.xzr.enc()); - try testing.expectEqual(@as(u5, 31), Register.wzr.enc()); - - try testing.expectEqual(@as(u5, 31), Register.sp.enc()); - try testing.expectEqual(@as(u5, 31), Register.sp.enc()); -} - -test "Register.size" { - try testing.expectEqual(@as(u7, 64), Register.x19.size()); - try testing.expectEqual(@as(u7, 32), Register.w3.size()); -} - -test "Register.to64/to32" { - try testing.expectEqual(Register.x0, Register.w0.to64()); - try testing.expectEqual(Register.x0, Register.x0.to64()); - - try testing.expectEqual(Register.w3, Register.w3.to32()); - try testing.expectEqual(Register.w3, Register.x3.to32()); -} - -// zig fmt: off - -/// Scalar floating point registers in the aarch64 instruction set -pub const FloatingPointRegister = enum(u8) { - // 128-bit registers + // 128-bit floating-point registers q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, q10, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, q21, q22, q23, q24, q25, q26, q27, q28, q29, q30, q31, - // 64-bit registers + // 64-bit floating-point registers d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, - // 32-bit registers + // 32-bit floating-point registers s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, - // 16-bit registers + // 16-bit floating-point registers h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11, h12, h13, h14, h15, h16, h17, h18, h19, h20, h21, h22, h23, h24, h25, h26, h27, h28, h29, h30, h31, - // 8-bit registers + // 8-bit floating-point registers b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, + // zig fmt: on - pub fn id(self: FloatingPointRegister) u5 { - return @truncate(u5, @enumToInt(self)); + pub fn class(self: Register) RegisterClass { + return switch (@enumToInt(self)) { + @enumToInt(Register.x0)...@enumToInt(Register.xzr) => .general_purpose, + @enumToInt(Register.w0)...@enumToInt(Register.wzr) => .general_purpose, + + @enumToInt(Register.sp) => .stack_pointer, + @enumToInt(Register.wsp) => .stack_pointer, + + @enumToInt(Register.q0)...@enumToInt(Register.q31) => .floating_point, + @enumToInt(Register.d0)...@enumToInt(Register.d31) => .floating_point, + @enumToInt(Register.s0)...@enumToInt(Register.s31) => .floating_point, + @enumToInt(Register.h0)...@enumToInt(Register.h31) => .floating_point, + @enumToInt(Register.b0)...@enumToInt(Register.b31) => .floating_point, + else => unreachable, + }; + } + + pub fn id(self: Register) u6 { + return switch (@enumToInt(self)) { + @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.x0)), + @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.w0)), + + @enumToInt(Register.sp) => 32, + @enumToInt(Register.wsp) => 32, + + @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.q0) + 33), + @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.d0) + 33), + @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.s0) + 33), + @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.h0) + 33), + @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intCast(u6, @enumToInt(self) - @enumToInt(Register.b0) + 33), + else => unreachable, + }; + } + + pub fn enc(self: Register) u5 { + return switch (@enumToInt(self)) { + @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.x0)), + @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.w0)), + + @enumToInt(Register.sp) => 31, + @enumToInt(Register.wsp) => 31, + + @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.q0)), + @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.d0)), + @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.s0)), + @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.h0)), + @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intCast(u5, @enumToInt(self) - @enumToInt(Register.b0)), + else => unreachable, + }; } /// Returns the bit-width of the register. - pub fn size(self: FloatingPointRegister) u8 { + pub fn size(self: Register) u8 { + return switch (@enumToInt(self)) { + @enumToInt(Register.x0)...@enumToInt(Register.xzr) => 64, + @enumToInt(Register.w0)...@enumToInt(Register.wzr) => 32, + + @enumToInt(Register.sp) => 64, + @enumToInt(Register.wsp) => 32, + + @enumToInt(Register.q0)...@enumToInt(Register.q31) => 128, + @enumToInt(Register.d0)...@enumToInt(Register.d31) => 64, + @enumToInt(Register.s0)...@enumToInt(Register.s31) => 32, + @enumToInt(Register.h0)...@enumToInt(Register.h31) => 16, + @enumToInt(Register.b0)...@enumToInt(Register.b31) => 8, + else => unreachable, + }; + } + + /// Convert from a general-purpose register to its 64 bit alias. + pub fn toX(self: Register) Register { + return switch (@enumToInt(self)) { + @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.x0) + @enumToInt(Register.x0), + ), + @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.w0) + @enumToInt(Register.x0), + ), + else => unreachable, + }; + } + + /// Convert from a general-purpose register to its 32 bit alias. + pub fn toW(self: Register) Register { return switch (@enumToInt(self)) { - 0...31 => 128, - 32...63 => 64, - 64...95 => 32, - 96...127 => 16, - 128...159 => 8, + @enumToInt(Register.x0)...@enumToInt(Register.xzr) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.x0) + @enumToInt(Register.w0), + ), + @enumToInt(Register.w0)...@enumToInt(Register.wzr) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.w0) + @enumToInt(Register.w0), + ), else => unreachable, }; } - /// Convert from any register to its 128 bit alias. - pub fn to128(self: FloatingPointRegister) FloatingPointRegister { - return @intToEnum(FloatingPointRegister, self.id()); + /// Convert from a floating-point register to its 128 bit alias. + pub fn toQ(self: Register) Register { + return switch (@enumToInt(self)) { + @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.q0), + ), + @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.q0), + ), + @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.q0), + ), + @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.q0), + ), + @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.q0), + ), + else => unreachable, + }; + } + + /// Convert from a floating-point register to its 64 bit alias. + pub fn toD(self: Register) Register { + return switch (@enumToInt(self)) { + @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.d0), + ), + @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.d0), + ), + @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.d0), + ), + @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.d0), + ), + @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.d0), + ), + else => unreachable, + }; } - /// Convert from any register to its 64 bit alias. - pub fn to64(self: FloatingPointRegister) FloatingPointRegister { - return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 32); + /// Convert from a floating-point register to its 32 bit alias. + pub fn toS(self: Register) Register { + return switch (@enumToInt(self)) { + @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.s0), + ), + @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.s0), + ), + @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.s0), + ), + @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.s0), + ), + @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.s0), + ), + else => unreachable, + }; } - /// Convert from any register to its 32 bit alias. - pub fn to32(self: FloatingPointRegister) FloatingPointRegister { - return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64); + /// Convert from a floating-point register to its 16 bit alias. + pub fn toH(self: Register) Register { + return switch (@enumToInt(self)) { + @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.h0), + ), + @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.h0), + ), + @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.h0), + ), + @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.h0), + ), + @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.h0), + ), + else => unreachable, + }; } - /// Convert from any register to its 16 bit alias. - pub fn to16(self: FloatingPointRegister) FloatingPointRegister { - return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 96); + /// Convert from a floating-point register to its 8 bit alias. + pub fn toB(self: Register) Register { + return switch (@enumToInt(self)) { + @enumToInt(Register.q0)...@enumToInt(Register.q31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.q0) + @enumToInt(Register.b0), + ), + @enumToInt(Register.d0)...@enumToInt(Register.d31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.d0) + @enumToInt(Register.b0), + ), + @enumToInt(Register.s0)...@enumToInt(Register.s31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.s0) + @enumToInt(Register.b0), + ), + @enumToInt(Register.h0)...@enumToInt(Register.h31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.h0) + @enumToInt(Register.b0), + ), + @enumToInt(Register.b0)...@enumToInt(Register.b31) => @intToEnum( + Register, + @enumToInt(self) - @enumToInt(Register.b0) + @enumToInt(Register.b0), + ), + else => unreachable, + }; } - /// Convert from any register to its 8 bit alias. - pub fn to8(self: FloatingPointRegister) FloatingPointRegister { - return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128); + pub fn dwarfLocOp(self: Register) u8 { + return @as(u8, self.enc()) + DW.OP.reg0; } }; -// zig fmt: on +test "Register.enc" { + try testing.expectEqual(@as(u5, 0), Register.x0.enc()); + try testing.expectEqual(@as(u5, 0), Register.w0.enc()); -test "FloatingPointRegister.id" { - try testing.expectEqual(@as(u5, 0), FloatingPointRegister.b0.id()); - try testing.expectEqual(@as(u5, 0), FloatingPointRegister.h0.id()); - try testing.expectEqual(@as(u5, 0), FloatingPointRegister.s0.id()); - try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.id()); - try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.id()); + try testing.expectEqual(@as(u5, 31), Register.xzr.enc()); + try testing.expectEqual(@as(u5, 31), Register.wzr.enc()); - try testing.expectEqual(@as(u5, 2), FloatingPointRegister.q2.id()); - try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id()); + try testing.expectEqual(@as(u5, 31), Register.sp.enc()); + try testing.expectEqual(@as(u5, 31), Register.sp.enc()); } -test "FloatingPointRegister.size" { - try testing.expectEqual(@as(u8, 128), FloatingPointRegister.q1.size()); - try testing.expectEqual(@as(u8, 64), FloatingPointRegister.d2.size()); - try testing.expectEqual(@as(u8, 32), FloatingPointRegister.s3.size()); - try testing.expectEqual(@as(u8, 16), FloatingPointRegister.h4.size()); - try testing.expectEqual(@as(u8, 8), FloatingPointRegister.b5.size()); +test "Register.size" { + try testing.expectEqual(@as(u8, 64), Register.x19.size()); + try testing.expectEqual(@as(u8, 32), Register.w3.size()); } -test "FloatingPointRegister.toX" { - try testing.expectEqual(FloatingPointRegister.q1, FloatingPointRegister.q1.to128()); - try testing.expectEqual(FloatingPointRegister.q2, FloatingPointRegister.b2.to128()); - try testing.expectEqual(FloatingPointRegister.q3, FloatingPointRegister.h3.to128()); +test "Register.toX/toW" { + try testing.expectEqual(Register.x0, Register.w0.toX()); + try testing.expectEqual(Register.x0, Register.x0.toX()); - try testing.expectEqual(FloatingPointRegister.d0, FloatingPointRegister.q0.to64()); - try testing.expectEqual(FloatingPointRegister.s1, FloatingPointRegister.d1.to32()); - try testing.expectEqual(FloatingPointRegister.h2, FloatingPointRegister.s2.to16()); - try testing.expectEqual(FloatingPointRegister.b3, FloatingPointRegister.h3.to8()); + try testing.expectEqual(Register.w3, Register.w3.toW()); + try testing.expectEqual(Register.w3, Register.x3.toW()); } /// Represents an instruction in the AArch64 instruction set |
