From eca0727417833b6c60cb2b30ba492ef816f4a4bc Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 9 Dec 2020 07:56:49 +0100 Subject: stage2+aarch64: use stp and ldp to navigate MachO jump table --- src/codegen.zig | 55 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 26 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index f1e1d2b8e4..240b400e59 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2730,13 +2730,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // For MachO, the binary, with the exception of object files, has to be a PIE. // Therefore we cannot load an absolute address. // Instead, we need to make use of PC-relative addressing. - // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack - // like described here: - // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop) - // TODO As far as branching is concerned, instead of saving the return address - // in a register, I'm thinking here of immitating x86_64, and having the address - // passed on the stack. if (reg.id() == 0) { // x0 is special-cased + // TODO This needs to be optimised in the stack usage (perhaps use a shadow stack + // like described here: + // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop) // str x28, [sp, #-16] mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{ .offset = Instruction.Offset.imm_pre_index(-16), @@ -2755,21 +2752,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // b [label] mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); // mov r, x0 - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr( + reg, + .x0, + Instruction.RegisterShift.none(), + ).toU32()); // ldr x28, [sp], #16 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{ .rn = Register.sp, .offset = Instruction.Offset.imm_post_index(16), }).toU32()); } else { - // str x28, [sp, #-16] - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{ - .offset = Instruction.Offset.imm_pre_index(-16), - }).toU32()); - // str x0, [sp, #-16] - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x0, Register.sp, .{ - .offset = Instruction.Offset.imm_pre_index(-16), - }).toU32()); + // stp x0, x28, [sp, #-16] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp( + .x0, + .x28, + Register.sp, + -2, + .SignedOffset, + ).toU32()); // adr x28, #8 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); if (self.bin_file.cast(link.File.MachO)) |macho_file| { @@ -2784,17 +2785,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // b [label] mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(0).toU32()); // mov r, x0 - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(reg, .x0, Instruction.RegisterShift.none()).toU32()); - // ldr x0, [sp], #16 - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x0, .{ - .rn = Register.sp, - .offset = Instruction.Offset.imm_post_index(16), - }).toU32()); - // ldr x28, [sp], #16 - mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{ - .rn = Register.sp, - .offset = Instruction.Offset.imm_post_index(16), - }).toU32()); + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr( + reg, + .x0, + Instruction.RegisterShift.none(), + ).toU32()); + // ldp x0, x28, [sp, #16] + mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp( + .x0, + .x28, + Register.sp, + 2, + .SignedOffset, + ).toU32()); } } else { // The value is in memory at a hard-coded address. -- cgit v1.2.3 From e91dbab256354477fdac0849f43766f43a7df8f0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 9 Dec 2020 08:07:47 +0100 Subject: stage2+aarch64: fix stage2 tests --- src/codegen.zig | 8 ++++---- src/codegen/aarch64.zig | 42 +++++++++++++++++++++--------------------- 2 files changed, 25 insertions(+), 25 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 240b400e59..8dfda6ceae 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2768,8 +2768,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .x0, .x28, Register.sp, - -2, - .SignedOffset, + -16, + .PreIndex, ).toU32()); // adr x28, #8 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); @@ -2795,8 +2795,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .x0, .x28, Register.sp, - 2, - .SignedOffset, + 16, + .PostIndex, ).toU32()); } } else { diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig index 17e1fab259..43d0054163 100644 --- a/src/codegen/aarch64.zig +++ b/src/codegen/aarch64.zig @@ -560,19 +560,20 @@ pub const Instruction = union(enum) { rt1: Register, rt2: Register, rn: Register, - imm7: i7, + offset: i9, encoding: u2, load: bool, ) Instruction { - const imm7_u: u7 = @bitCast(u7, imm7); switch (rt1.size()) { 32 => { + assert(-256 <= offset and offset <= 252); + const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); return Instruction{ .LoadStorePairOfRegisters = .{ .rt1 = rt1.id(), .rn = rn.id(), .rt2 = rt2.id(), - .imm7 = imm7_u, + .imm7 = imm7, .load = @boolToInt(load), .encoding = encoding, .opc = 0b00, @@ -580,12 +581,14 @@ pub const Instruction = union(enum) { }; }, 64 => { + assert(-512 <= offset and offset <= 504); + const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); return Instruction{ .LoadStorePairOfRegisters = .{ .rt1 = rt1.id(), .rn = rn.id(), .rt2 = rt2.id(), - .imm7 = imm7_u, + .imm7 = imm7, .load = @boolToInt(load), .encoding = encoding, .opc = 0b10, @@ -731,20 +734,20 @@ pub const Instruction = union(enum) { SignedOffset = 0b10, PreIndex = 0b11, }; - pub fn ldp(rt1: Register, rt2: Register, rn: Register, imm: i7, encoding: LoadStorePairEncoding) Instruction { - return loadStorePairOfRegisters(rt1, rt2, rn, imm, @enumToInt(encoding), true); + pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: i9, encoding: LoadStorePairEncoding) Instruction { + return loadStorePairOfRegisters(rt1, rt2, rn, offset, @enumToInt(encoding), true); } - pub fn ldnp(rt1: Register, rt2: Register, rn: Register, imm: i7) Instruction { - return loadStorePairOfRegisters(rt1, rt2, rn, imm, 0, true); + pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { + return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, true); } - pub fn stp(rt1: Register, rt2: Register, rn: Register, imm: i7, encoding: LoadStorePairEncoding) Instruction { - return loadStorePairOfRegisters(rt1, rt2, rn, imm, @enumToInt(encoding), false); + pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: i9, encoding: LoadStorePairEncoding) Instruction { + return loadStorePairOfRegisters(rt1, rt2, rn, offset, @enumToInt(encoding), false); } - pub fn stnp(rt1: Register, rt2: Register, rn: Register, imm: i7) Instruction { - return loadStorePairOfRegisters(rt1, rt2, rn, imm, 0, false); + pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { + return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, false); } // Exception generation @@ -903,29 +906,26 @@ test "serialize instructions" { .inst = Instruction.adrp(.x2, -0x8), .expected = 0b1_00_10000_1111111111111111110_00010, }, - .{ // stp x1, x2, [sp, #1] - .inst = Instruction.stp(.x1, .x2, Register.sp, 1, .SignedOffset), + .{ // stp x1, x2, [sp, #8] + .inst = Instruction.stp(.x1, .x2, Register.sp, 8, .SignedOffset), .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, }, .{ // stp x1, x2, [sp, #-16] .inst = Instruction.stp(.x1, .x2, Register.sp, -16, .SignedOffset), - .expected = 0b10_101_0_010_0_1110000_00010_11111_00001, + .expected = 0b10_101_0_010_0_1111110_00010_11111_00001, }, - .{ // ldp x1, x2, [sp, #1] - .inst = Instruction.ldp(.x1, .x2, Register.sp, 1, .SignedOffset), + .{ // ldp x1, x2, [sp, #8] + .inst = Instruction.ldp(.x1, .x2, Register.sp, 8, .SignedOffset), .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, }, .{ // ldp x1, x2, [sp, #16] .inst = Instruction.ldp(.x1, .x2, Register.sp, 16, .SignedOffset), - .expected = 0b10_101_0_010_1_0010000_00010_11111_00001, + .expected = 0b10_101_0_010_1_0000010_00010_11111_00001, }, }; for (testcases) |case| { const actual = case.inst.toU32(); - if (case.expected != actual) { - std.debug.print("0b{b} != 0b{b}\n", .{ case.expected, actual }); - } testing.expectEqual(case.expected, actual); } } -- cgit v1.2.3 From 2082c275577163a3c3006ae15b0c8af51a414f7d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 9 Dec 2020 17:15:00 +0100 Subject: stage2+aarch64: clean up offset helper structs --- src/codegen.zig | 10 +- src/codegen/aarch64.zig | 304 ++++++++++++++++++++++++++---------------------- 2 files changed, 167 insertions(+), 147 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 8dfda6ceae..3b0a383a71 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2736,7 +2736,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/using-the-stack-in-aarch64-implementing-push-and-pop) // str x28, [sp, #-16] mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.str(.x28, Register.sp, .{ - .offset = Instruction.Offset.imm_pre_index(-16), + .offset = Instruction.LoadStoreOffset.imm_pre_index(-16), }).toU32()); // adr x28, #8 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); @@ -2760,7 +2760,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // ldr x28, [sp], #16 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{ .rn = Register.sp, - .offset = Instruction.Offset.imm_post_index(16), + .offset = Instruction.LoadStoreOffset.imm_post_index(16), }).toU32()); } else { // stp x0, x28, [sp, #-16] @@ -2768,8 +2768,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .x0, .x28, Register.sp, - -16, - .PreIndex, + Instruction.LoadStorePairOffset.pre_index(-16), ).toU32()); // adr x28, #8 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32()); @@ -2795,8 +2794,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .x0, .x28, Register.sp, - 16, - .PostIndex, + Instruction.LoadStorePairOffset.post_index(16), ).toU32()); } } else { diff --git a/src/codegen/aarch64.zig b/src/codegen/aarch64.zig index 43d0054163..0e9ad61745 100644 --- a/src/codegen/aarch64.zig +++ b/src/codegen/aarch64.zig @@ -290,123 +290,6 @@ pub const Instruction = union(enum) { }; } - /// 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 Offset = 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 = Offset{ - .Immediate = .{ .Unsigned = 0 }, - }; - - pub fn toU12(self: Offset) 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) Offset { - return Offset{ - .Immediate = .{ .Unsigned = offset }, - }; - } - - pub fn imm_post_index(offset: i9) Offset { - return Offset{ - .Immediate = .{ .PostIndex = offset }, - }; - } - - pub fn imm_pre_index(offset: i9) Offset { - return Offset{ - .Immediate = .{ .PreIndex = offset }, - }; - } - - pub fn reg(rm: Register) Offset { - return Offset{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Lsl = 0, - }, - }, - }; - } - - pub fn reg_uxtw(rm: Register, shift: u2) Offset { - assert(rm.size() == 32 and (shift == 0 or shift == 2)); - return Offset{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Uxtw = shift, - }, - }, - }; - } - - pub fn reg_lsl(rm: Register, shift: u2) Offset { - assert(rm.size() == 64 and (shift == 0 or shift == 3)); - return Offset{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Lsl = shift, - }, - }, - }; - } - - pub fn reg_sxtw(rm: Register, shift: u2) Offset { - assert(rm.size() == 32 and (shift == 0 or shift == 2)); - return Offset{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Sxtw = shift, - }, - }, - }; - } - - pub fn reg_sxtx(rm: Register, shift: u2) Offset { - assert(rm.size() == 64 and (shift == 0 or shift == 3)); - return Offset{ - .Register = .{ - .rm = rm.id(), - .shift = .{ - .Sxtx = shift, - }, - }, - }; - } - }; - pub const RegisterShift = struct { rn: u5, imm6: u6, @@ -514,7 +397,124 @@ pub const Instruction = union(enum) { }; } - fn loadStoreRegister(rt: Register, rn: Register, offset: Offset, load: bool) Instruction { + /// 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, + }, + }, + }; + } + }; + + fn loadStoreRegister(rt: Register, rn: Register, offset: LoadStoreOffset, load: bool) Instruction { const off = offset.toU12(); const op1: u2 = blk: { switch (offset) { @@ -709,9 +709,10 @@ pub const Instruction = union(enum) { pub const LdrArgs = struct { rn: ?Register = null, - offset: Offset = Offset.none, + offset: LoadStoreOffset = LoadStoreOffset.none, literal: ?u19 = null, }; + pub fn ldr(rt: Register, args: LdrArgs) Instruction { if (args.rn) |rn| { return loadStoreRegister(rt, rn, args.offset, true); @@ -721,29 +722,50 @@ pub const Instruction = union(enum) { } pub const StrArgs = struct { - offset: Offset = Offset.none, + offset: LoadStoreOffset = LoadStoreOffset.none, }; + pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { return loadStoreRegister(rt, rn, args.offset, false); } // Load or store pair of registers - pub const LoadStorePairEncoding = enum(u2) { - PostIndex = 0b01, - SignedOffset = 0b10, - PreIndex = 0b11, + 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: i9, encoding: LoadStorePairEncoding) Instruction { - return loadStorePairOfRegisters(rt1, rt2, rn, offset, @enumToInt(encoding), true); + + pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { + return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); } pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, true); } - pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: i9, encoding: LoadStorePairEncoding) Instruction { - return loadStorePairOfRegisters(rt1, rt2, rn, offset, @enumToInt(encoding), false); + pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { + return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); } pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { @@ -867,15 +889,15 @@ test "serialize instructions" { .expected = 0b11_111_0_01_01_000000000000_00001_00010, }, .{ // ldr x2, [x1, #1]! - .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.Offset.imm_pre_index(1) }), + .inst = Instruction.ldr(.x2, .{ .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, .{ .rn = .x1, .offset = Instruction.Offset.imm_post_index(-1) }), + .inst = Instruction.ldr(.x2, .{ .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, .{ .rn = .x1, .offset = Instruction.Offset.reg(.x3) }), + .inst = Instruction.ldr(.x2, .{ .rn = .x1, .offset = Instruction.LoadStoreOffset.reg(.x3) }), .expected = 0b11_111_0_00_01_1_00011_011_0_10_00001_00010, }, .{ // ldr x2, label @@ -887,7 +909,7 @@ test "serialize instructions" { .expected = 0b11_111_0_01_00_000000000000_00001_00010, }, .{ // str x2, [x1], (x3) - .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.Offset.reg(.x3) }), + .inst = Instruction.str(.x2, .x1, .{ .offset = Instruction.LoadStoreOffset.reg(.x3) }), .expected = 0b11_111_0_00_00_1_00011_011_0_10_00001_00010, }, .{ // adr x2, #0x8 @@ -907,20 +929,20 @@ test "serialize instructions" { .expected = 0b1_00_10000_1111111111111111110_00010, }, .{ // stp x1, x2, [sp, #8] - .inst = Instruction.stp(.x1, .x2, Register.sp, 8, .SignedOffset), + .inst = Instruction.stp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), .expected = 0b10_101_0_010_0_0000001_00010_11111_00001, }, - .{ // stp x1, x2, [sp, #-16] - .inst = Instruction.stp(.x1, .x2, Register.sp, -16, .SignedOffset), - .expected = 0b10_101_0_010_0_1111110_00010_11111_00001, - }, .{ // ldp x1, x2, [sp, #8] - .inst = Instruction.ldp(.x1, .x2, Register.sp, 8, .SignedOffset), + .inst = Instruction.ldp(.x1, .x2, Register.sp, Instruction.LoadStorePairOffset.signed(8)), .expected = 0b10_101_0_010_1_0000001_00010_11111_00001, }, - .{ // ldp x1, x2, [sp, #16] - .inst = Instruction.ldp(.x1, .x2, Register.sp, 16, .SignedOffset), - .expected = 0b10_101_0_010_1_0000010_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, }, }; -- cgit v1.2.3