aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/aarch64.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-09-30 21:38:04 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-09-30 21:38:04 -0700
commit3eb729b442d6cc69bd9a9d37816e9458190bc30b (patch)
treea412281c18914503c1566e79ab6a6f4eea187bf3 /src/codegen/aarch64.zig
parent1f653b7f8e9e358a5bfe2695a11c01da56f3d5ee (diff)
parentc4cd592f0e1eeff5a4056796610d97010ae4e38c (diff)
downloadzig-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.zig1230
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);
- }
-}