diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-09-30 21:38:04 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-09-30 21:38:04 -0700 |
| commit | 3eb729b442d6cc69bd9a9d37816e9458190bc30b (patch) | |
| tree | a412281c18914503c1566e79ab6a6f4eea187bf3 /src/codegen/aarch64.zig | |
| parent | 1f653b7f8e9e358a5bfe2695a11c01da56f3d5ee (diff) | |
| parent | c4cd592f0e1eeff5a4056796610d97010ae4e38c (diff) | |
| download | zig-3eb729b442d6cc69bd9a9d37816e9458190bc30b.tar.gz zig-3eb729b442d6cc69bd9a9d37816e9458190bc30b.zip | |
Merge remote-tracking branch 'origin/master' into llvm13
Diffstat (limited to 'src/codegen/aarch64.zig')
| -rw-r--r-- | src/codegen/aarch64.zig | 1230 |
1 files changed, 0 insertions, 1230 deletions
diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig deleted file mode 100644 index dfda04da85..0000000000 --- a/src/codegen/aarch64.zig +++ /dev/null @@ -1,1230 +0,0 @@ -const std = @import("std"); -const DW = std.dwarf; -const assert = std.debug.assert; -const testing = std.testing; - -// zig fmt: off - -/// General purpose registers in the AArch64 instruction set -pub const Register = enum(u6) { - // 64-bit 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 - w0, w1, w2, w3, w4, w5, w6, w7, - w8, w9, w10, w11, w12, w13, w14, w15, - w16, w17, w18, w19, w20, w21, w22, w23, - w24, w25, w26, w27, w28, w29, w30, wzr, - - pub const sp = Register.xzr; - - pub fn id(self: Register) u5 { - return @truncate(u5, @enumToInt(self)); - } - - /// Returns the bit-width of the register. - pub fn size(self: Register) u7 { - return switch (@enumToInt(self)) { - 0...31 => 64, - 32...63 => 32, - }; - } - - /// 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(u6, self.id()) + 32); - } - - /// Returns the index into `callee_preserved_regs`. - pub fn allocIndex(self: Register) ?u4 { - inline for (callee_preserved_regs) |cpreg, i| { - if (self.id() == cpreg.id()) return i; - } - return null; - } - - pub fn dwarfLocOp(self: Register) u8 { - return @as(u8, self.id()) + DW.OP.reg0; - } -}; - -// zig fmt: on - -pub const callee_preserved_regs = [_]Register{ - .x19, .x20, .x21, .x22, .x23, - .x24, .x25, .x26, .x27, .x28, -}; - -pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; -pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 }; - -test "Register.id" { - try testing.expectEqual(@as(u5, 0), Register.x0.id()); - try testing.expectEqual(@as(u5, 0), Register.w0.id()); - - try testing.expectEqual(@as(u5, 31), Register.xzr.id()); - try testing.expectEqual(@as(u5, 31), Register.wzr.id()); - - try testing.expectEqual(@as(u5, 31), Register.sp.id()); - try testing.expectEqual(@as(u5, 31), Register.sp.id()); -} - -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 - 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()); -} - -/// Represents an instruction in the AArch64 instruction set -pub const Instruction = union(enum) { - move_wide_immediate: packed struct { - rd: u5, - imm16: u16, - hw: u2, - fixed: u6 = 0b100101, - opc: u2, - sf: u1, - }, - pc_relative_address: packed struct { - rd: u5, - immhi: u19, - fixed: u5 = 0b10000, - immlo: u2, - op: u1, - }, - load_store_register: packed struct { - rt: u5, - rn: u5, - offset: u12, - opc: u2, - op1: u2, - v: u1, - fixed: u3 = 0b111, - size: u2, - }, - load_store_register_pair: packed struct { - rt1: u5, - rn: u5, - rt2: u5, - imm7: u7, - load: u1, - encoding: u2, - fixed: u5 = 0b101_0_0, - opc: u2, - }, - load_literal: packed struct { - rt: u5, - imm19: u19, - fixed: u6 = 0b011_0_00, - opc: u2, - }, - exception_generation: packed struct { - ll: u2, - op2: u3, - imm16: u16, - opc: u3, - fixed: u8 = 0b1101_0100, - }, - unconditional_branch_register: packed struct { - op4: u5, - rn: u5, - op3: u6, - op2: u5, - opc: u4, - fixed: u7 = 0b1101_011, - }, - unconditional_branch_immediate: packed struct { - imm26: u26, - fixed: u5 = 0b00101, - op: u1, - }, - no_operation: packed struct { - fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, - }, - logical_shifted_register: packed struct { - rd: u5, - rn: u5, - imm6: u6, - rm: u5, - n: u1, - shift: u2, - fixed: u5 = 0b01010, - opc: u2, - sf: u1, - }, - add_subtract_immediate: packed struct { - rd: u5, - rn: u5, - imm12: u12, - sh: u1, - fixed: u6 = 0b100010, - s: u1, - op: u1, - sf: u1, - }, - conditional_branch: struct { - cond: u4, - o0: u1, - imm19: u19, - o1: u1, - fixed: u7 = 0b0101010, - }, - compare_and_branch: struct { - rt: u5, - imm19: u19, - op: u1, - fixed: u6 = 0b011010, - sf: u1, - }, - - pub const Shift = struct { - shift: Type = .lsl, - amount: u6 = 0, - - pub const Type = enum(u2) { - lsl, - lsr, - asr, - ror, - }; - - pub const none = Shift{ - .shift = .lsl, - .amount = 0, - }; - }; - - pub const Condition = enum(u4) { - /// Integer: Equal - /// Floating point: Equal - eq, - /// Integer: Not equal - /// Floating point: Not equal or unordered - ne, - /// Integer: Carry set - /// Floating point: Greater than, equal, or unordered - cs, - /// Integer: Carry clear - /// Floating point: Less than - cc, - /// Integer: Minus, negative - /// Floating point: Less than - mi, - /// Integer: Plus, positive or zero - /// Floating point: Greater than, equal, or unordered - pl, - /// Integer: Overflow - /// Floating point: Unordered - vs, - /// Integer: No overflow - /// Floating point: Ordered - vc, - /// Integer: Unsigned higher - /// Floating point: Greater than, or unordered - hi, - /// Integer: Unsigned lower or same - /// Floating point: Less than or equal - ls, - /// Integer: Signed greater than or equal - /// Floating point: Greater than or equal - ge, - /// Integer: Signed less than - /// Floating point: Less than, or unordered - lt, - /// Integer: Signed greater than - /// Floating point: Greater than - gt, - /// Integer: Signed less than or equal - /// Floating point: Less than, equal, or unordered - le, - /// Integer: Always - /// Floating point: Always - al, - /// Integer: Always - /// Floating point: Always - nv, - }; - - pub fn toU32(self: Instruction) u32 { - return switch (self) { - .move_wide_immediate => |v| @bitCast(u32, v), - .pc_relative_address => |v| @bitCast(u32, v), - .load_store_register => |v| @bitCast(u32, v), - .load_store_register_pair => |v| @bitCast(u32, v), - .load_literal => |v| @bitCast(u32, v), - .exception_generation => |v| @bitCast(u32, v), - .unconditional_branch_register => |v| @bitCast(u32, v), - .unconditional_branch_immediate => |v| @bitCast(u32, v), - .no_operation => |v| @bitCast(u32, v), - .logical_shifted_register => |v| @bitCast(u32, v), - .add_subtract_immediate => |v| @bitCast(u32, v), - // TODO once packed structs work, this can be refactored - .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), - .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), - }; - } - - fn moveWideImmediate( - opc: u2, - rd: Register, - imm16: u16, - shift: u6, - ) Instruction { - switch (rd.size()) { - 32 => { - assert(shift % 16 == 0 and shift <= 16); - return Instruction{ - .move_wide_immediate = .{ - .rd = rd.id(), - .imm16 = imm16, - .hw = @intCast(u2, shift / 16), - .opc = opc, - .sf = 0, - }, - }; - }, - 64 => { - assert(shift % 16 == 0 and shift <= 48); - return Instruction{ - .move_wide_immediate = .{ - .rd = rd.id(), - .imm16 = imm16, - .hw = @intCast(u2, shift / 16), - .opc = opc, - .sf = 1, - }, - }; - }, - else => unreachable, // unexpected register size - } - } - - fn pcRelativeAddress(rd: Register, imm21: i21, op: u1) Instruction { - assert(rd.size() == 64); - const imm21_u = @bitCast(u21, imm21); - return Instruction{ - .pc_relative_address = .{ - .rd = rd.id(), - .immlo = @truncate(u2, imm21_u), - .immhi = @truncate(u19, imm21_u >> 2), - .op = op, - }, - }; - } - - /// Represents the offset operand of a load or store instruction. - /// Data can be loaded from memory with either an immediate offset - /// or an offset that is stored in some register. - pub const LoadStoreOffset = union(enum) { - Immediate: union(enum) { - PostIndex: i9, - PreIndex: i9, - Unsigned: u12, - }, - Register: struct { - rm: u5, - shift: union(enum) { - Uxtw: u2, - Lsl: u2, - Sxtw: u2, - Sxtx: u2, - }, - }, - - pub const none = LoadStoreOffset{ - .Immediate = .{ .Unsigned = 0 }, - }; - - pub fn toU12(self: LoadStoreOffset) u12 { - return switch (self) { - .Immediate => |imm_type| switch (imm_type) { - .PostIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 1, - .PreIndex => |v| (@intCast(u12, @bitCast(u9, v)) << 2) + 3, - .Unsigned => |v| v, - }, - .Register => |r| switch (r.shift) { - .Uxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 16 + 2050, - .Lsl => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 24 + 2050, - .Sxtw => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 48 + 2050, - .Sxtx => |v| (@intCast(u12, r.rm) << 6) + (@intCast(u12, v) << 2) + 56 + 2050, - }, - }; - } - - pub fn imm(offset: u12) LoadStoreOffset { - return .{ - .Immediate = .{ .Unsigned = offset }, - }; - } - - pub fn imm_post_index(offset: i9) LoadStoreOffset { - return .{ - .Immediate = .{ .PostIndex = offset }, - }; - } - - pub fn imm_pre_index(offset: i9) LoadStoreOffset { - return .{ - .Immediate = .{ .PreIndex = offset }, - }; - } - - pub fn reg(rm: Register) LoadStoreOffset { - return .{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Lsl = 0, - }, - }, - }; - } - - pub fn reg_uxtw(rm: Register, shift: u2) LoadStoreOffset { - assert(rm.size() == 32 and (shift == 0 or shift == 2)); - return .{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Uxtw = shift, - }, - }, - }; - } - - pub fn reg_lsl(rm: Register, shift: u2) LoadStoreOffset { - assert(rm.size() == 64 and (shift == 0 or shift == 3)); - return .{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Lsl = shift, - }, - }, - }; - } - - pub fn reg_sxtw(rm: Register, shift: u2) LoadStoreOffset { - assert(rm.size() == 32 and (shift == 0 or shift == 2)); - return .{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Sxtw = shift, - }, - }, - }; - } - - pub fn reg_sxtx(rm: Register, shift: u2) LoadStoreOffset { - assert(rm.size() == 64 and (shift == 0 or shift == 3)); - return .{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Sxtx = shift, - }, - }, - }; - } - }; - - /// Which kind of load/store to perform - const LoadStoreVariant = enum { - /// 32-bit or 64-bit - str, - /// 16-bit, zero-extended - strh, - /// 8-bit, zero-extended - strb, - /// 32-bit or 64-bit - ldr, - /// 16-bit, zero-extended - ldrh, - /// 8-bit, zero-extended - ldrb, - }; - - fn loadStoreRegister( - rt: Register, - rn: Register, - offset: LoadStoreOffset, - variant: LoadStoreVariant, - ) Instruction { - const off = offset.toU12(); - const op1: u2 = blk: { - switch (offset) { - .Immediate => |imm| switch (imm) { - .Unsigned => break :blk 0b01, - else => {}, - }, - else => {}, - } - break :blk 0b00; - }; - const opc: u2 = switch (variant) { - .ldr, .ldrh, .ldrb => 0b01, - .str, .strh, .strb => 0b00, - }; - return Instruction{ - .load_store_register = .{ - .rt = rt.id(), - .rn = rn.id(), - .offset = off, - .opc = opc, - .op1 = op1, - .v = 0, - .size = blk: { - switch (variant) { - .ldr, .str => switch (rt.size()) { - 32 => break :blk 0b10, - 64 => break :blk 0b11, - else => unreachable, // unexpected register size - }, - .ldrh, .strh => break :blk 0b01, - .ldrb, .strb => break :blk 0b00, - } - }, - }, - }; - } - - fn loadStoreRegisterPair( - rt1: Register, - rt2: Register, - rn: Register, - offset: i9, - encoding: u2, - load: bool, - ) Instruction { - switch (rt1.size()) { - 32 => { - assert(-256 <= offset and offset <= 252); - const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); - return Instruction{ - .load_store_register_pair = .{ - .rt1 = rt1.id(), - .rn = rn.id(), - .rt2 = rt2.id(), - .imm7 = imm7, - .load = @boolToInt(load), - .encoding = encoding, - .opc = 0b00, - }, - }; - }, - 64 => { - assert(-512 <= offset and offset <= 504); - const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); - return Instruction{ - .load_store_register_pair = .{ - .rt1 = rt1.id(), - .rn = rn.id(), - .rt2 = rt2.id(), - .imm7 = imm7, - .load = @boolToInt(load), - .encoding = encoding, - .opc = 0b10, - }, - }; - }, - else => unreachable, // unexpected register size - } - } - - fn loadLiteral(rt: Register, imm19: u19) Instruction { - switch (rt.size()) { - 32 => { - return Instruction{ - .load_literal = .{ - .rt = rt.id(), - .imm19 = imm19, - .opc = 0b00, - }, - }; - }, - 64 => { - return Instruction{ - .load_literal = .{ - .rt = rt.id(), - .imm19 = imm19, - .opc = 0b01, - }, - }; - }, - else => unreachable, // unexpected register size - } - } - - fn exceptionGeneration( - opc: u3, - op2: u3, - ll: u2, - imm16: u16, - ) Instruction { - return Instruction{ - .exception_generation = .{ - .ll = ll, - .op2 = op2, - .imm16 = imm16, - .opc = opc, - }, - }; - } - - fn unconditionalBranchRegister( - opc: u4, - op2: u5, - op3: u6, - rn: Register, - op4: u5, - ) Instruction { - assert(rn.size() == 64); - - return Instruction{ - .unconditional_branch_register = .{ - .op4 = op4, - .rn = rn.id(), - .op3 = op3, - .op2 = op2, - .opc = opc, - }, - }; - } - - fn unconditionalBranchImmediate( - op: u1, - offset: i28, - ) Instruction { - return Instruction{ - .unconditional_branch_immediate = .{ - .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)), - .op = op, - }, - }; - } - - fn logicalShiftedRegister( - opc: u2, - n: u1, - shift: Shift, - rd: Register, - rn: Register, - rm: Register, - ) Instruction { - switch (rd.size()) { - 32 => { - assert(shift.amount < 32); - return Instruction{ - .logical_shifted_register = .{ - .rd = rd.id(), - .rn = rn.id(), - .imm6 = shift.amount, - .rm = rm.id(), - .n = n, - .shift = @enumToInt(shift.shift), - .opc = opc, - .sf = 0b0, - }, - }; - }, - 64 => { - return Instruction{ - .logical_shifted_register = .{ - .rd = rd.id(), - .rn = rn.id(), - .imm6 = shift.amount, - .rm = rm.id(), - .n = n, - .shift = @enumToInt(shift.shift), - .opc = opc, - .sf = 0b1, - }, - }; - }, - else => unreachable, // unexpected register size - } - } - - fn addSubtractImmediate( - op: u1, - s: u1, - rd: Register, - rn: Register, - imm12: u12, - shift: bool, - ) Instruction { - return Instruction{ - .add_subtract_immediate = .{ - .rd = rd.id(), - .rn = rn.id(), - .imm12 = imm12, - .sh = @boolToInt(shift), - .s = s, - .op = op, - .sf = switch (rd.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - fn conditionalBranch( - o0: u1, - o1: u1, - cond: Condition, - offset: i21, - ) Instruction { - assert(offset & 0b11 == 0b00); - return Instruction{ - .conditional_branch = .{ - .cond = @enumToInt(cond), - .o0 = o0, - .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), - .o1 = o1, - }, - }; - } - - fn compareAndBranch( - op: u1, - rt: Register, - offset: i21, - ) Instruction { - assert(offset & 0b11 == 0b00); - return Instruction{ - .compare_and_branch = .{ - .rt = rt.id(), - .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), - .op = op, - .sf = switch (rt.size()) { - 32 => 0b0, - 64 => 0b1, - else => unreachable, // unexpected register size - }, - }, - }; - } - - // Helper functions for assembly syntax functions - - // Move wide (immediate) - - pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction { - return moveWideImmediate(0b00, rd, imm16, shift); - } - - pub fn movz(rd: Register, imm16: u16, shift: u6) Instruction { - return moveWideImmediate(0b10, rd, imm16, shift); - } - - pub fn movk(rd: Register, imm16: u16, shift: u6) Instruction { - return moveWideImmediate(0b11, rd, imm16, shift); - } - - // PC relative address - - pub fn adr(rd: Register, imm21: i21) Instruction { - return pcRelativeAddress(rd, imm21, 0b0); - } - - pub fn adrp(rd: Register, imm21: i21) Instruction { - return pcRelativeAddress(rd, imm21, 0b1); - } - - // Load or store register - - pub const LdrArgs = union(enum) { - register: struct { - rn: Register, - offset: LoadStoreOffset = LoadStoreOffset.none, - }, - literal: u19, - }; - - pub fn ldr(rt: Register, args: LdrArgs) Instruction { - switch (args) { - .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .ldr), - .literal => |literal| return loadLiteral(rt, literal), - } - } - - pub fn ldrh(rt: Register, rn: Register, args: StrArgs) Instruction { - return loadStoreRegister(rt, rn, args.offset, .ldrh); - } - - pub fn ldrb(rt: Register, rn: Register, args: StrArgs) Instruction { - return loadStoreRegister(rt, rn, args.offset, .ldrb); - } - - pub const StrArgs = struct { - offset: LoadStoreOffset = LoadStoreOffset.none, - }; - - pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { - return loadStoreRegister(rt, rn, args.offset, .str); - } - - pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction { - return loadStoreRegister(rt, rn, args.offset, .strh); - } - - pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction { - return loadStoreRegister(rt, rn, args.offset, .strb); - } - - // Load or store pair of registers - - pub const LoadStorePairOffset = struct { - encoding: enum(u2) { - PostIndex = 0b01, - Signed = 0b10, - PreIndex = 0b11, - }, - offset: i9, - - pub fn none() LoadStorePairOffset { - return .{ .encoding = .Signed, .offset = 0 }; - } - - pub fn post_index(imm: i9) LoadStorePairOffset { - return .{ .encoding = .PostIndex, .offset = imm }; - } - - pub fn pre_index(imm: i9) LoadStorePairOffset { - return .{ .encoding = .PreIndex, .offset = imm }; - } - - pub fn signed(imm: i9) LoadStorePairOffset { - return .{ .encoding = .Signed, .offset = imm }; - } - }; - - pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { - return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); - } - - pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { - return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true); - } - - pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { - return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); - } - - pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { - return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false); - } - - // Exception generation - - pub fn svc(imm16: u16) Instruction { - return exceptionGeneration(0b000, 0b000, 0b01, imm16); - } - - pub fn hvc(imm16: u16) Instruction { - return exceptionGeneration(0b000, 0b000, 0b10, imm16); - } - - pub fn smc(imm16: u16) Instruction { - return exceptionGeneration(0b000, 0b000, 0b11, imm16); - } - - pub fn brk(imm16: u16) Instruction { - return exceptionGeneration(0b001, 0b000, 0b00, imm16); - } - - pub fn hlt(imm16: u16) Instruction { - return exceptionGeneration(0b010, 0b000, 0b00, imm16); - } - - // Unconditional branch (register) - - pub fn br(rn: Register) Instruction { - return unconditionalBranchRegister(0b0000, 0b11111, 0b000000, rn, 0b00000); - } - - pub fn blr(rn: Register) Instruction { - return unconditionalBranchRegister(0b0001, 0b11111, 0b000000, rn, 0b00000); - } - - pub fn ret(rn: ?Register) Instruction { - return unconditionalBranchRegister(0b0010, 0b11111, 0b000000, rn orelse .x30, 0b00000); - } - - // Unconditional branch (immediate) - - pub fn b(offset: i28) Instruction { - return unconditionalBranchImmediate(0, offset); - } - - pub fn bl(offset: i28) Instruction { - return unconditionalBranchImmediate(1, offset); - } - - // Nop - - pub fn nop() Instruction { - return Instruction{ .no_operation = .{} }; - } - - // Logical (shifted register) - - pub fn @"and"(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b00, 0b0, shift, rd, rn, rm); - } - - pub fn bic(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b00, 0b1, shift, rd, rn, rm); - } - - pub fn orr(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b01, 0b0, shift, rd, rn, rm); - } - - pub fn orn(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b01, 0b1, shift, rd, rn, rm); - } - - pub fn eor(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b10, 0b0, shift, rd, rn, rm); - } - - pub fn eon(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b10, 0b1, shift, rd, rn, rm); - } - - pub fn ands(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b11, 0b0, shift, rd, rn, rm); - } - - pub fn bics(rd: Register, rn: Register, rm: Register, shift: Shift) Instruction { - return logicalShiftedRegister(0b11, 0b1, shift, rd, rn, rm); - } - - // Add/subtract (immediate) - - pub fn add(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { - return addSubtractImmediate(0b0, 0b0, rd, rn, imm, shift); - } - - pub fn adds(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { - return addSubtractImmediate(0b0, 0b1, rd, rn, imm, shift); - } - - pub fn sub(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { - return addSubtractImmediate(0b1, 0b0, rd, rn, imm, shift); - } - - pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { - return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift); - } - - // Conditional branch - - pub fn bCond(cond: Condition, offset: i21) Instruction { - return conditionalBranch(0b0, 0b0, cond, offset); - } - - // Compare and branch - - pub fn cbz(rt: Register, offset: i21) Instruction { - return compareAndBranch(0b0, rt, offset); - } - - pub fn cbnz(rt: Register, offset: i21) Instruction { - return compareAndBranch(0b1, rt, offset); - } -}; - -test { - testing.refAllDecls(@This()); -} - -test "serialize instructions" { - const Testcase = struct { - inst: Instruction, - expected: u32, - }; - - const testcases = [_]Testcase{ - .{ // orr x0, xzr, x1 - .inst = Instruction.orr(.x0, .xzr, .x1, Instruction.Shift.none), - .expected = 0b1_01_01010_00_0_00001_000000_11111_00000, - }, - .{ // orn x0, xzr, x1 - .inst = Instruction.orn(.x0, .xzr, .x1, Instruction.Shift.none), - .expected = 0b1_01_01010_00_1_00001_000000_11111_00000, - }, - .{ // movz x1, #4 - .inst = Instruction.movz(.x1, 4, 0), - .expected = 0b1_10_100101_00_0000000000000100_00001, - }, - .{ // movz x1, #4, lsl 16 - .inst = Instruction.movz(.x1, 4, 16), - .expected = 0b1_10_100101_01_0000000000000100_00001, - }, - .{ // movz x1, #4, lsl 32 - .inst = Instruction.movz(.x1, 4, 32), - .expected = 0b1_10_100101_10_0000000000000100_00001, - }, - .{ // movz x1, #4, lsl 48 - .inst = Instruction.movz(.x1, 4, 48), - .expected = 0b1_10_100101_11_0000000000000100_00001, - }, - .{ // movz w1, #4 - .inst = Instruction.movz(.w1, 4, 0), - .expected = 0b0_10_100101_00_0000000000000100_00001, - }, - .{ // movz w1, #4, lsl 16 - .inst = Instruction.movz(.w1, 4, 16), - .expected = 0b0_10_100101_01_0000000000000100_00001, - }, - .{ // svc #0 - .inst = Instruction.svc(0), - .expected = 0b1101_0100_000_0000000000000000_00001, - }, - .{ // svc #0x80 ; typical on Darwin - .inst = Instruction.svc(0x80), - .expected = 0b1101_0100_000_0000000010000000_00001, - }, - .{ // ret - .inst = Instruction.ret(null), - .expected = 0b1101_011_00_10_11111_0000_00_11110_00000, - }, - .{ // bl #0x10 - .inst = Instruction.bl(0x10), - .expected = 0b1_00101_00_0000_0000_0000_0000_0000_0100, - }, - .{ // ldr x2, [x1] - .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1 } }), - .expected = 0b11_111_0_01_01_000000000000_00001_00010, - }, - .{ // ldr x2, [x1, #1]! - .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_pre_index(1) } }), - .expected = 0b11_111_0_00_01_0_000000001_11_00001_00010, - }, - .{ // ldr x2, [x1], #-1 - .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.imm_post_index(-1) } }), - .expected = 0b11_111_0_00_01_0_111111111_01_00001_00010, - }, - .{ // ldr x2, [x1], (x3) - .inst = Instruction.ldr(.x2, .{ .register = .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) } }), - .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, - }, - .{ // ldr x2, label - .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }), - .expected = 0b01_011_0_00_0000000000000000001_00010, - }, - .{ // ldrh x7, [x4], #0xaa - .inst = Instruction.ldrh(.x7, .x4, .{ .offset = Instruction.LoadStoreOffset.imm_post_index(0xaa) }), - .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111, - }, - .{ // ldrb x9, [x15, #0xff]! - .inst = Instruction.ldrb(.x9, .x15, .{ .offset = Instruction.LoadStoreOffset.imm_pre_index(0xff) }), - .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001, - }, - .{ // str x2, [x1] - .inst = Instruction.str(.x2, .x1, .{}), - .expected = 0b11_111_0_01_00_000000000000_00001_00010, - }, - .{ // str x2, [x1], (x3) - .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), - .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, - }, - .{ // strh w0, [x1] - .inst = Instruction.strh(.w0, .x1, .{}), - .expected = 0b01_111_0_01_00_000000000000_00001_00000, - }, - .{ // strb w8, [x9] - .inst = Instruction.strb(.w8, .x9, .{}), - .expected = 0b00_111_0_01_00_000000000000_01001_01000, - }, - .{ // adr x2, #0x8 - .inst = Instruction.adr(.x2, 0x8), - .expected = 0b0_00_10000_0000000000000000010_00010, - }, - .{ // adr x2, -#0x8 - .inst = Instruction.adr(.x2, -0x8), - .expected = 0b0_00_10000_1111111111111111110_00010, - }, - .{ // adrp x2, #0x8 - .inst = Instruction.adrp(.x2, 0x8), - .expected = 0b1_00_10000_0000000000000000010_00010, - }, - .{ // adrp x2, -#0x8 - .inst = Instruction.adrp(.x2, -0x8), - .expected = 0b1_00_10000_1111111111111111110_00010, - }, - .{ // stp x1, x2, [sp, #8] - .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), - .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, - }, - .{ // ldp x1, x2, [sp, #8] - .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), - .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, - }, - .{ // stp x1, x2, [sp, #-16]! - .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.pre_index(-16)), - .expected = 0b10_101_0_011_0_1111110_00010_11111_00001, - }, - .{ // ldp x1, x2, [sp], #16 - .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.post_index(16)), - .expected = 0b10_101_0_001_1_0000010_00010_11111_00001, - }, - .{ // and x0, x4, x2 - .inst = Instruction.@"and"(.x0, .x4, .x2, .{}), - .expected = 0b1_00_01010_00_0_00010_000000_00100_00000, - }, - .{ // and x0, x4, x2, lsl #0x8 - .inst = Instruction.@"and"(.x0, .x4, .x2, .{ .shift = .lsl, .amount = 0x8 }), - .expected = 0b1_00_01010_00_0_00010_001000_00100_00000, - }, - .{ // add x0, x10, #10 - .inst = Instruction.add(.x0, .x10, 10, false), - .expected = 0b1_0_0_100010_0_0000_0000_1010_01010_00000, - }, - .{ // subs x0, x5, #11, lsl #12 - .inst = Instruction.subs(.x0, .x5, 11, true), - .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000, - }, - .{ // b.hi #-4 - .inst = Instruction.bCond(.hi, -4), - .expected = 0b0101010_0_1111111111111111111_0_1000, - }, - .{ // cbz x10, #40 - .inst = Instruction.cbz(.x10, 40), - .expected = 0b1_011010_0_0000000000000001010_01010, - }, - }; - - for (testcases) |case| { - const actual = case.inst.toU32(); - try testing.expectEqual(case.expected, actual); - } -} |
