stage2 AArch64: merge floating-point registers into Register enum

This commit is contained in:
joachimschmidt557 2022-09-11 21:10:18 +02:00
parent b41b35f578
commit 151e15e444
No known key found for this signature in database
GPG Key ID: E0B575BE2884ACC5
3 changed files with 339 additions and 213 deletions

View File

@ -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,
};
},
}
}

View File

@ -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),
));
},

View File

@ -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,51 +28,267 @@ pub const Register = enum(u7) {
// Stack pointer
sp, wsp,
// 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 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 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 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 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 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)) {
0...63 => return @as(u6, @truncate(u5, @enumToInt(self))),
64...65 => 32,
@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)) {
0...63 => return @truncate(u5, @enumToInt(self)),
64...65 => 31,
@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: Register) u7 {
pub fn size(self: Register) u8 {
return switch (@enumToInt(self)) {
0...31 => 64,
32...63 => 32,
64 => 64,
65 => 32,
@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 any register to its 64 bit alias.
pub fn to64(self: Register) Register {
/// Convert from a general-purpose register to its 64 bit alias.
pub fn toX(self: Register) Register {
return switch (@enumToInt(self)) {
0...31 => self,
32...63 => @intToEnum(Register, @enumToInt(self) - 32),
64 => .sp,
65 => .sp,
@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 any register to its 32 bit alias.
pub fn to32(self: Register) Register {
/// Convert from a general-purpose register to its 32 bit alias.
pub fn toW(self: Register) Register {
return switch (@enumToInt(self)) {
0...31 => @intToEnum(Register, @enumToInt(self) + 32),
32...63 => self,
64 => .wsp,
65 => .wsp,
@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 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 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 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 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,
};
}
@ -77,8 +298,6 @@ pub const Register = enum(u7) {
}
};
// zig fmt: on
test "Register.enc" {
try testing.expectEqual(@as(u5, 0), Register.x0.enc());
try testing.expectEqual(@as(u5, 0), Register.w0.enc());
@ -91,124 +310,16 @@ test "Register.enc" {
}
test "Register.size" {
try testing.expectEqual(@as(u7, 64), Register.x19.size());
try testing.expectEqual(@as(u7, 32), Register.w3.size());
try testing.expectEqual(@as(u8, 64), Register.x19.size());
try testing.expectEqual(@as(u8, 32), Register.w3.size());
}
test "Register.to64/to32" {
try testing.expectEqual(Register.x0, Register.w0.to64());
try testing.expectEqual(Register.x0, Register.x0.to64());
test "Register.toX/toW" {
try testing.expectEqual(Register.x0, Register.w0.toX());
try testing.expectEqual(Register.x0, Register.x0.toX());
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
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
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
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
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
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,
pub fn id(self: FloatingPointRegister) u5 {
return @truncate(u5, @enumToInt(self));
}
/// Returns the bit-width of the register.
pub fn size(self: FloatingPointRegister) u8 {
return switch (@enumToInt(self)) {
0...31 => 128,
32...63 => 64,
64...95 => 32,
96...127 => 16,
128...159 => 8,
else => unreachable,
};
}
/// Convert from any register to its 128 bit alias.
pub fn to128(self: FloatingPointRegister) FloatingPointRegister {
return @intToEnum(FloatingPointRegister, self.id());
}
/// 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 any register to its 32 bit alias.
pub fn to32(self: FloatingPointRegister) FloatingPointRegister {
return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 64);
}
/// 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 any register to its 8 bit alias.
pub fn to8(self: FloatingPointRegister) FloatingPointRegister {
return @intToEnum(FloatingPointRegister, @as(u8, self.id()) + 128);
}
};
// zig fmt: on
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, 2), FloatingPointRegister.q2.id());
try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d31.id());
}
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 "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());
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