stage2 AArch64: add logical (shifted register) instructions
This commit is contained in:
committed by
Andrew Kelley
parent
6c0c275b27
commit
c5ec096b2f
@@ -2415,19 +2415,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
try self.genSetReg(inst.base.src, reg, arg);
|
||||
}
|
||||
|
||||
// TODO move this to lib/std/{elf, macho}.zig, etc.
|
||||
const is_syscall_inst = switch (self.bin_file.tag) {
|
||||
.macho => mem.eql(u8, inst.asm_source, "svc #0x80"),
|
||||
.elf => mem.eql(u8, inst.asm_source, "svc #0"),
|
||||
else => |tag| return self.fail(inst.base.src, "TODO implement aarch64 support for other syscall instructions for file format: '{}'", .{tag}),
|
||||
};
|
||||
if (is_syscall_inst) {
|
||||
const imm16: u16 = switch (self.bin_file.tag) {
|
||||
.macho => 0x80,
|
||||
.elf => 0,
|
||||
else => unreachable,
|
||||
};
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(imm16).toU32());
|
||||
if (mem.eql(u8, inst.asm_source, "svc #0")) {
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());
|
||||
} else if (mem.eql(u8, inst.asm_source, "svc #0x80")) {
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
|
||||
} else {
|
||||
return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});
|
||||
}
|
||||
@@ -2876,8 +2867,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
// mov r, x0
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
|
||||
reg,
|
||||
.xzr,
|
||||
.x0,
|
||||
Instruction.RegisterShift.none(),
|
||||
Instruction.Shift.none,
|
||||
).toU32());
|
||||
// ldr x28, [sp], #16
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.x28, .{
|
||||
@@ -2908,8 +2900,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
// mov r, x0
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
|
||||
reg,
|
||||
.xzr,
|
||||
.x0,
|
||||
Instruction.RegisterShift.none(),
|
||||
Instruction.Shift.none,
|
||||
).toU32());
|
||||
// ldp x0, x28, [sp, #16]
|
||||
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp(
|
||||
|
||||
@@ -200,17 +200,6 @@ test "FloatingPointRegister.toX" {
|
||||
|
||||
/// Represents an instruction in the AArch64 instruction set
|
||||
pub const Instruction = union(enum) {
|
||||
OrShiftedRegister: packed struct {
|
||||
rd: u5,
|
||||
rn: u5,
|
||||
imm6: u6,
|
||||
rm: u5,
|
||||
n: u1,
|
||||
shift: u2,
|
||||
fixed: u5 = 0b01010,
|
||||
opc: u2 = 0b01,
|
||||
sf: u1,
|
||||
},
|
||||
MoveWideImmediate: packed struct {
|
||||
rd: u5,
|
||||
imm16: u16,
|
||||
@@ -274,10 +263,37 @@ pub const Instruction = union(enum) {
|
||||
NoOperation: packed struct {
|
||||
fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111,
|
||||
},
|
||||
LogicalShiftedRegister: packed struct {
|
||||
rd: u5,
|
||||
rn: u5,
|
||||
imm6: u6,
|
||||
rm: u5,
|
||||
n: u1,
|
||||
shift: u2,
|
||||
fixed: u5 = 0b01010,
|
||||
opc: u2,
|
||||
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 fn toU32(self: Instruction) u32 {
|
||||
return switch (self) {
|
||||
.OrShiftedRegister => |v| @bitCast(u32, v),
|
||||
.MoveWideImmediate => |v| @bitCast(u32, v),
|
||||
.PCRelativeAddress => |v| @bitCast(u32, v),
|
||||
.LoadStoreRegister => |v| @bitCast(u32, v),
|
||||
@@ -287,68 +303,10 @@ pub const Instruction = union(enum) {
|
||||
.UnconditionalBranchRegister => |v| @bitCast(u32, v),
|
||||
.UnconditionalBranchImmediate => |v| @bitCast(u32, v),
|
||||
.NoOperation => |v| @bitCast(u32, v),
|
||||
.LogicalShiftedRegister => |v| @bitCast(u32, v),
|
||||
};
|
||||
}
|
||||
|
||||
pub const RegisterShift = struct {
|
||||
rn: u5,
|
||||
imm6: u6,
|
||||
shift: enum(u2) {
|
||||
Lsl = 0,
|
||||
Lsr = 1,
|
||||
Asr = 2,
|
||||
Ror = 3,
|
||||
},
|
||||
|
||||
pub fn none() RegisterShift {
|
||||
return .{
|
||||
.rn = 0b11111,
|
||||
.imm6 = 0,
|
||||
.shift = .Lsl,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Helper functions for assembly syntax functions
|
||||
|
||||
fn orShiftedRegister(
|
||||
rd: Register,
|
||||
rm: Register,
|
||||
shift: RegisterShift,
|
||||
invert: bool,
|
||||
) Instruction {
|
||||
const n: u1 = if (invert) 1 else 0;
|
||||
switch (rd.size()) {
|
||||
32 => {
|
||||
return Instruction{
|
||||
.OrShiftedRegister = .{
|
||||
.rd = rd.id(),
|
||||
.rn = shift.rn,
|
||||
.imm6 = shift.imm6,
|
||||
.rm = rm.id(),
|
||||
.n = n,
|
||||
.shift = @enumToInt(shift.shift),
|
||||
.sf = 0,
|
||||
},
|
||||
};
|
||||
},
|
||||
64 => {
|
||||
return Instruction{
|
||||
.OrShiftedRegister = .{
|
||||
.rd = rd.id(),
|
||||
.rn = shift.rn,
|
||||
.imm6 = shift.imm6,
|
||||
.rm = rm.id(),
|
||||
.n = n,
|
||||
.shift = @enumToInt(shift.shift),
|
||||
.sf = 1,
|
||||
},
|
||||
};
|
||||
},
|
||||
else => unreachable, // unexpected register size
|
||||
}
|
||||
}
|
||||
|
||||
fn moveWideImmediate(
|
||||
opc: u2,
|
||||
rd: Register,
|
||||
@@ -671,15 +629,49 @@ pub const Instruction = union(enum) {
|
||||
};
|
||||
}
|
||||
|
||||
// Bitwise (inclusive) OR of a register value
|
||||
|
||||
pub fn orr(rd: Register, rm: Register, shift: RegisterShift) Instruction {
|
||||
return orShiftedRegister(rd, rm, shift, false);
|
||||
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{
|
||||
.LogicalShiftedRegister = .{
|
||||
.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{
|
||||
.LogicalShiftedRegister = .{
|
||||
.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
|
||||
}
|
||||
}
|
||||
|
||||
pub fn orn(rd: Register, rm: Register, shift: RegisterShift) Instruction {
|
||||
return orShiftedRegister(rd, rm, shift, true);
|
||||
}
|
||||
// Helper functions for assembly syntax functions
|
||||
|
||||
// Move wide (immediate)
|
||||
|
||||
@@ -823,6 +815,40 @@ pub const Instruction = union(enum) {
|
||||
pub fn nop() Instruction {
|
||||
return Instruction{ .NoOperation = {} };
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
||||
test "" {
|
||||
@@ -836,15 +862,15 @@ test "serialize instructions" {
|
||||
};
|
||||
|
||||
const testcases = [_]Testcase{
|
||||
.{ // orr x0 x1
|
||||
.inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()),
|
||||
.{ // 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 x1
|
||||
.inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.none()),
|
||||
.{ // 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
|
||||
.{ // movz x1, #4
|
||||
.inst = Instruction.movz(.x1, 4, 0),
|
||||
.expected = 0b1_10_100101_00_0000000000000100_00001,
|
||||
},
|
||||
@@ -944,6 +970,14 @@ test "serialize instructions" {
|
||||
.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,
|
||||
},
|
||||
};
|
||||
|
||||
for (testcases) |case| {
|
||||
|
||||
Reference in New Issue
Block a user