stage2 macho: add orr and orn instructions

This commit is contained in:
Jakub Konka 2020-11-25 19:58:15 +01:00
parent 10942e3f86
commit c749b78df5
3 changed files with 147 additions and 11 deletions

View File

@ -2588,17 +2588,64 @@ 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.
// 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)
// 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
// 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());
// adr x28, #8
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{
.address = addr,
.start = self.code.items.len,
.len = 4,
});
// bl [label]
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());
// } else {
// unreachable; // TODO
// }
// 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 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());
// adr x28, #8
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.adr(.x28, 8).toU32());
try self.mod_fn.owner_decl.link.macho.addPieFixup(self.bin_file.allocator, .{
.address = addr,
.start = self.code.items.len,
.len = 4,
});
// 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());
}
} else {
// The value is in memory at a hard-coded address.
// If the type is a pointer, it means the pointer address is at this memory location.

View File

@ -19,6 +19,8 @@ pub const Register = enum(u6) {
w16, w17, w18, w19, w20, w21, w22, w23,
w24, w25, w26, w27, w28, w29, w30, wzr,
pub const sp = .xzr;
pub fn id(self: Register) u5 {
return @truncate(u5, @enumToInt(self));
}
@ -195,6 +197,17 @@ 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,
@ -251,6 +264,7 @@ pub const Instruction = union(enum) {
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),
@ -379,8 +393,65 @@ pub const Instruction = union(enum) {
}
};
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,
@ -543,6 +614,16 @@ 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);
}
pub fn orn(rd: Register, rm: Register, shift: RegisterShift) Instruction {
return orShiftedRegister(rd, rm, shift, true);
}
// Move wide (immediate)
pub fn movn(rd: Register, imm16: u16, shift: u6) Instruction {
@ -653,6 +734,14 @@ test "serialize instructions" {
};
const testcases = [_]Testcase{
.{ // orr x0 x1
.inst = Instruction.orr(.x0, .x1, Instruction.RegisterShift.none()),
.expected = 0b1_01_01010_00_0_00001_000000_11111_00000,
},
.{ // orn x0 x1
.inst = Instruction.orn(.x0, .x1, Instruction.RegisterShift.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,

View File

@ -1028,7 +1028,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
} else {
const displacement = @intCast(u27, target_addr - this_addr);
var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());
mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.b(@intCast(i28, displacement)).toU32());
}
}
@ -1670,10 +1670,10 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
} else {
const pos_symbol_off = @intCast(u20, vmaddr - self.offset_table.items[index]);
const symbol_off = @intCast(i21, pos_symbol_off) * -1;
// adr .x0 [-disp]
mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x1, symbol_off).toU32());
// ret
mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(null).toU32());
// adr x0, #-disp
mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x0, symbol_off).toU32());
// ret x28
mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ret(.x28).toU32());
}
log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off });
try self.base.file.?.pwriteAll(&code, off);